A Spring Boot 3 REST API for managing library operations. It enables librarians to manage publications (books and journals) and their cover images, while allowing members to borrow and return items. The system also provides advanced statistical endpoints for analyzing borrowing trends. The system demonstrates solid object-oriented design, modern Java 25 features, and an in-memory persistence layer — no external database required.
The Library Management Service provides RESTful endpoints for managing loans, members, publications, and library analytics. It allows:
- Librarians to manage the publication catalog (Books and Journals), including creating, updating, deleting, and uploading cover images.
- Members to borrow one or multiple available items, return selected or all items, and view loan details.
- System Analysts to view statistical reports, such as top borrowed items, overdue lists, and rolling monthly activity trends.
- Publication Management: Full CRUD operations for books and journals with support for localized titles.
- Image Handling: Upload and serve cover images using NIO.2 with strict validation and path traversal protection.
- Loan Processing: Checkout items with concurrent availability checks and track due dates.
- Statistical Reporting: Dedicated endpoints for analytics, including top borrowers, yearly trends, and rolling averages using Gatherers.
- Concurrency: Parallel processing during checkout to verify item availability efficiently.
- Localization: Internationalization (i18n) support for error messages and publication titles.
- In-Memory Storage: Uses thread-safe maps for persistence, eliminating the need for an external database.
- Auto-initialized Data: Preloaded sample data for members, publications, and historical loans.
This API follows a three-layer architecture:
Each layer has a clear responsibility:
- Controller: Handles HTTP requests and responses.
- Service: Contains business logic for borrowing and returning items.
- Repository: Manages in-memory storage using static maps.
The application provides interactive API documentation using SpringDoc OpenAPI 3.
📍 Access Points
- Swagger UI: http://localhost:8080/swagger-ui.html
- OpenAPI JSON: http://localhost:8080/api-docs
- OpenAPI YAML: http://localhost:8080/api-docs.yaml
The OpenAPI documentation is configured in application.yml:
springdoc:
api-docs:
path: /api-docs
groups:
enabled: true
swagger-ui:
path: /swagger-ui.html
operationsSorter: method
tagsSorter: alpha
disable-swagger-default-url: true- Interactive Testing: Execute API calls directly from the browser
- Schema Documentation: Complete request/response model documentation
- Error Responses: All possible HTTP status codes and error formats
- Parameter Documentation: Detailed parameter descriptions and examples
POST /v1/members/{memberId}/loans
Create a new loan for a member by borrowing one or more available items.
{
"items": [1, 2]
}{
"id": 1,
"memberId": 1,
"loanDate": "2025-10-25",
"expectedReturnDate": "2025-11-08",
"items": [
{ "id": 1, "title": "Clean Code", "type": "BOOK" },
{ "id": 2, "title": "Nature Neuroscience", "type": "JOURNAL" }
],
"status": "OPEN"
}{
"timestamp": "2025-10-25T15:30:00",
"status": 409,
"error": "Conflict",
"message": "Item 'Clean Code' is currently loaned out",
"path": "/v1/members/1/loans"
}GET /v1/members/{memberId}/loans
Retrieve a summary of all loans for a member.
[
{
"loanId": 1,
"loanDate": "2025-10-25",
"expectedReturnDate": "2025-11-08",
"status": "OPEN"
}
]GET /v1/loans/{loanId}
Fetch detailed information about a specific loan and its items.
{
"id": 1,
"memberId": 1,
"loanDate": "2025-10-25",
"expectedReturnDate": "2025-11-08",
"items": [
{ "id": 1, "title": "Clean Code", "type": "BOOK" }
],
"status": "OPEN"
}POST /v1/loans/{loanId}/returns
Return one or more items, or all if no body is provided.
{
"items": [1]
}{
"id": 1,
"memberId": 1,
"loanDate": "2025-10-25",
"expectedReturnDate": "2025-11-08",
"items": [
{ "id": 1, "title": "Clean Code", "type": "BOOK", "returnedDate": "2025-10-30" }
],
"status": "CLOSED"
}POST /v1/publications
Create a new book or journal entry.
{
"type": "BOOK",
"title": "Domain-Driven Design",
"author": "Eric Evans",
"publicationDate": "2003-09-30",
"category": "TECHNOLOGY",
"isbn": "9780321125217",
"genre": "Software Architecture",
"pageCount": 560
}{
"id": 100,
"type": "BOOK",
"title": "Domain-Driven Design",
"author": "Eric Evans",
"available": true,
"isbn": "9780321125217",
"genre": "Software Architecture",
"pageCount": 560
}GET /v1/publications
Retrieve all publications. Supports optional filtering by category.
Query Parameters
category(optional): Filter by category (e.g.,TECHNOLOGY,SCIENCE).
[
{
"id": 1,
"type": "BOOK",
"title": "Clean Code",
"author": "Robert C. Martin",
"available": true
}
]GET /v1/publications/{id}
Retrieve detailed information about a specific publication.
{
"id": 1,
"type": "BOOK",
"title": "Clean Code",
"author": "Robert C. Martin",
"publicationDate": "2008-08-01",
"category": "TECHNOLOGY",
"available": true,
"isbn": "9780132350884",
"genre": "Programming",
"pageCount": 464
}PUT /v1/publications/{id}
Update an existing publication. The type in the request must match the stored type.
{
"type": "BOOK",
"title": "Clean Code: A Handbook of Agile Software Craftsmanship",
"author": "Robert C. Martin",
"publicationDate": "2008-08-01",
"category": "TECHNOLOGY",
"isbn": "9780132350884",
"genre": "Software Engineering",
"pageCount": 464
}DELETE /v1/publications/{id}
Delete a publication. Returns 204 No Content. Fails with 409 Conflict if the item is currently on loan.
POST /v1/publications/{id}/image
Upload or replace the cover image for a publication.
- Content-Type: multipart/form-data
- Parameter: file (File)
{
"id": 1,
"coverImageFileName": "cover_1_1714123200000.jpg",
...
}GET /v1/publications/{id}/image
Retrieve the cover image file.
- Content-Type:
image/jpeg,image/png, orimage/webp. - Body: Raw image bytes.
GET /v1/statistics/members/borrowers
Returns a sorted list of all unique member names who have ever borrowed an item.
[
"Alice Johnson",
"Bob Williams",
"Charlie Davis"
]GET /v1/statistics/items/top-borrowed
Returns the most frequently borrowed items across all time.
topN(optional): Number of results (default 10).
[
{ "itemId": 1, "title": "Clean Code", "type": "BOOK", "loanCount": 15 }
]GET /v1/statistics/categories/loan-counts
Returns categories ranked by how many times their items were borrowed.
[
{ "category": "TECHNOLOGY", "loanCount": 50 },
{ "category": "SCIENCE", "loanCount": 30 }
]GET /v1/statistics/members/top-borrowers/year/{year}
Returns members ranked by items borrowed in the given year, most active first.
year: The year to analyze (e.g.,2024).
[
{
"memberId": 1,
"firstName": "Alice",
"lastName": "Johnson",
"email": "alice@example.com",
"totalLoans": 12,
"totalItems": 25
}
]GET /v1/statistics/items/borrowed/year/{year}
Returns a lightweight summary of items borrowed in a given year.
[
{ "itemId": 1, "title": "Clean Code", "type": "BOOK", "loanCount": 8 }
]GET /v1/statistics/loans/yearly-trend
Returns total items loaned per calendar year across all history.
[
{ "year": 2024, "loanCount": 120 },
{ "year": 2025, "loanCount": 85 }
]GET /v1/statistics/loans/overdue
Returns all unreturned items past their due date, sorted by days overdue descending.
[
{
"loanId": 50,
"memberId": 2,
"memberName": "Bob Williams",
"itemId": 5,
"itemTitle": "Effective Java",
"expectedReturnDate": "2025-11-01",
"daysOverdue": 15
}
]GET /v1/statistics/loans/avg-items
Returns the all-time average number of items per loan transaction.
2.5GET /v1/statistics/members/{memberId}/has-active-loans
Returns true if the specified member currently has at least one open loan.
trueGET /v1/statistics/members/monthly-activity
Returns per-member monthly loan counts with a sliding-window rolling average.
windowSize(optional): Number of months for the rolling window (default 3).
{
"1": [
{ "year": 2025, "month": 10, "loanCount": 2, "rollingAvg": 2.0 },
{ "year": 2025, "month": 11, "loanCount": 4, "rollingAvg": 3.0 }
]
} The project includes comprehensive Javadoc documentation for all classes, methods, and records.
# Generate Javadoc
mvn javadoc:javadoc
# View generated documentation
open target/site/apidocs/index.htmlAfter generation, open target/site/apidocs/index.html in your browser to view:
- Class Documentation: Detailed descriptions of all entities, services, and controllers
- Method Documentation: Complete parameter and return value documentation
- Record Documentation: DTO field descriptions and usage examples
- Exception Documentation: When and why each exception is thrown
- Inheritance Trees: Class hierarchy and implementation relationships
- Complete Coverage: All public and protected methods documented
- Cross-References: @see tags linking related classes and methods
- Parameter Validation: Documentation of constraints and validation rules
- Exception Handling: Detailed @throws documentation
- Return Value Descriptions: Clear explanations of what each method returns
| Principle | Description | Example |
|---|---|---|
| Encapsulation | Data hidden behind getters/setters | LibraryItem fields are private |
| Inheritance | Common behavior in base classes | Book and Journal extend LibraryItem |
| Polymorphism | Shared interface with different implementations | getType() returns "BOOK" or "JOURNAL" |
| Abstraction | Abstract parent defines contract | LibraryItem is an abstract sealed class |
| Interfaces | Business contracts | LoanService defines loan operations |
| Feature | Example | Description |
|---|---|---|
| Sorting | .sorted(Comparator.comparingInt( LoanStreakStats::streak).reversed()) |
Sorting collections using Comparator references |
| Lambdas | Callable<LoanItem> task = () -> { ... } |
Usage of functional interfaces (Callable, Function) |
| Streams (Intermediate) | .filter(...), .map(...), .flatMap(...), .distinct(), .limit(...) |
Intermediate operations to transform or filter data streams |
| Streams (Terminal) | .collect(...), .anyMatch(...), .max(...), .forEach(...), findFirst(), count() |
Terminal operations to produce results or side-effects |
| Collectors | Collectors.toMap(...), Collectors.groupingBy(...) |
Mutable reduction operations for maps and grouping |
| Switch Expressions | Used for modern control flow | Simplified and expressive switch syntax |
| Sealed Classes | public sealed abstract class LibraryItem permits Book, Journal {} |
Restricts subclassing for type safety |
| Records | public record LoanResponse(...) {} |
Compact immutable data transfer obj ects |
| Date/Time API | plusDays(...), ChronoUnit.DAYS.between(...) |
Modern date/time handling |
| Feature | Example | Description |
|---|---|---|
| Concurrency | ExecutorService executor = Executors.newFixedThreadPool(threads); |
Managing concurrent tasks using thread pools and Callables |
| NIO.2 | Files.createDirectories(...), Files.copy(...), Path.resolve(...) |
Modern non-blocking I/O operations for robust file handling |
| Localization | ReloadableResourceBundleMessageSource, LocaleResolver |
Internationalization (i18n) support via message bundles |
| Gatherers | .gather(Gatherer.ofSequential(...)) |
Custom intermediate operations for stateful stream processing |
| JEP | Feature | Example in Code |
|---|---|---|
| JEP 481 | Scoped Values | CorrelationIdFilter uses ScopedValue for request context propagation |
| JEP 485 | Stream Gatherers | StatisticsServiceImpl uses custom gatherers for rolling averages |
| JEP 513 | Flexible Constructor Bodies | Book class constructor with validation before super() |
| JEP 512 | Compact Source Files and Instance Main | Application class with simplified main() method |
JEP 481: Scoped Values
// CorrelationIdFilter.java
ScopedValue.where(RequestContext.CORRELATION_ID, finalCorrelationId)
.run(() -> LoggingContext.runWithLoggingContext(() -> { ... }));JEP 513: Flexible Constructor Bodies
public class Book extends LibraryItem {
public Book(String title, String author, LocalDate publicationDate, String isbn, String genre, int pageCount) {
super(title, author, publicationDate);
if (pageCount <= 0) {
throw new IllegalArgumentException("Page count must be positive");
}
this.isbn = isbn;
this.genre = genre;
this.pageCount = pageCount;
}
}JEP 512: Compact Source Files and Instance Main
@SpringBootApplication
public class LibraryManagementApplication {
public static void main(String[] args) {
SpringApplication.run(LibraryManagementApplication.class, args);
}
}This project replaces JPA/H2 with a thread-safe in-memory approach:
- Uses ConcurrentHashMap as storage for each repository.
- Uses AtomicLong to auto-generate IDs.
- Loads mock data via
@PostConstructinLibraryBootstrap:- 3 members: Alice, Bob, Charlie
- Multiple publications: Books and Journals across various categories (Tech, Science, Fiction).
- Historical Loans: Pre-seeded loans across multiple years (2024-2025) to support statistical analysis.
Example repository initialization:
@PostConstruct
void seed() {
memberData.getMembers().forEach(memberRepository::save);
itemData.getBooks().forEach(itemRepository::save);
// ...
}- Java 25+
- Maven 3.9+
mvn spring-boot:runhttp://localhost:8080/v1
This project includes both unit tests and integration tests using Karate DSL to ensure correctness and reliability of the Library Management Service.
Unit tests validate the behavior of individual components such as services and repositories. They are written using JUnit 5 and cover core business logic like loan creation, item return, and exception handling.
mvn test
This command will execute all unit tests located in the src/test/java directory. Results will be displayed in the console and stored in the target/surefire-reports folder.
Karate is a powerful DSL for testing REST APIs. It allows you to write expressive, scenario-based tests that validate the full request/response lifecycle.
- Verify end-to-end flows for borrowing and returning items.
- Ensure the API behaves correctly under different conditions.
- Simulate real-world usage with dynamic data and assertions.
- Full Borrow and Return Flow Borrow items, save loan ID, return items, assert loan is closed and returnedDate is set.
- Partial Return Flow Borrow items 3 and 5, return item 3, then item 5 — validating intermediate loan states.
mvn -Dtest=com.lms.library.karate.BorrowAndReturnBooksTest testThis command runs the Karate feature file located at classpath:karate/BorrowAndReturnBooks.feature.
After execution, a detailed HTML report is generated at:
target/karate-reports/karate-summary.html
This project is for educational and demonstration purposes — showing clean architecture, Java 25 features, and OOP principles in a Spring Boot REST API.





