A REST API for managing a library catalog and circulation (checkout/return), built with ASP.NET Core 8, Clean Architecture, and MediatR/CQRS.
| Layer | Technology |
|---|---|
| Framework | .NET 8, ASP.NET Core 8 |
| Database | PostgreSQL 16 (EF Core 8) |
| CQRS / Mediator | MediatR 12 |
| Validation | FluentValidation |
| API Docs | Swagger / Swashbuckle |
| Testing | xUnit, FluentAssertions, WebApplicationFactory |
| Containerization | Docker Compose |
Clean Architecture with four projects — dependencies point strictly inward:
LibraryManagement.Domain ← entities, exceptions (no dependencies)
LibraryManagement.Application ← commands, queries, validators (MediatR + FluentValidation)
LibraryManagement.Infrastructure ← EF Core, PostgreSQL, migrations
LibraryManagement.API ← controllers, middleware, DI composition root
classDiagram
class BaseEntity {
<<abstract>>
+Guid Id
+DateTime CreatedAt
+DateTime UpdatedAt
}
class Author { +string Name, +string? Bio }
class Genre { +string Name }
class Publisher { +string Name }
class Book { +string Title, +string Isbn, +int PublicationYear, +int TotalCopies, +int AvailableCopies }
class Member { +string FullName, +string Email, +string? PhoneNumber, +bool IsActive }
class Loan { +DateTime LoanDate, +DateTime DueDate, +DateTime? ReturnDate, +bool IsReturned }
BaseEntity <|-- Author
BaseEntity <|-- Genre
BaseEntity <|-- Publisher
BaseEntity <|-- Book
BaseEntity <|-- Member
BaseEntity <|-- Loan
Author "1" --> "0..*" Book
Genre "1" --> "0..*" Book
Publisher "1" --> "0..*" Book
Book "1" --> "0..*" Loan
Member "1" --> "0..*" Loan
| Method | Route | Description |
|---|---|---|
| GET | /api/Authors |
List authors (paginated, sortable, filterable by name) |
| GET | /api/Authors/{id} |
Get author by ID |
| POST | /api/Authors |
Create author |
| PUT | /api/Authors/{id} |
Update author |
| DELETE | /api/Authors/{id} |
Delete author (blocked if author has books) |
| GET/POST/PUT/DELETE | /api/Books |
CRUD — filter by title, ISBN, author, genre, year, availability |
| GET/POST/PUT/DELETE | /api/Genres |
CRUD |
| GET/POST/PUT/DELETE | /api/Publishers |
CRUD |
| Method | Route | Description |
|---|---|---|
| GET/POST/PUT/DELETE | /api/Members |
CRUD — filter by name, email, active status |
| Method | Route | Description |
|---|---|---|
| POST | /api/Loans |
Checkout a book (requires available copies + active member) |
| POST | /api/Loans/{id}/return |
Return a book |
| GET | /api/Loans |
List loans — filter by member, book, returned, overdue |
| GET | /api/Loans/{id} |
Get loan by ID |
All list endpoints return a PagedResult<T> envelope:
{
"items": [...],
"pageNumber": 1,
"pageSize": 10,
"totalCount": 42,
"totalPages": 5
}Query parameters (all optional):
| Parameter | Default | Description |
|---|---|---|
pageNumber |
1 | Page to fetch |
pageSize |
10 | Items per page (max 50) |
sortBy |
createdAt |
Sort field (whitelisted per endpoint) |
sortDescending |
false | Sort direction |
| entity-specific | e.g. title, isbn, authorId, isAvailable for Books |
Example:
GET /api/Books?title=1984&sortBy=title&sortDescending=false&pageNumber=1&pageSize=5
GET /api/Loans?isOverdue=true&sortBy=dueDate
GET /api/Authors?name=Orwell&sortBy=name
docker compose up -d
# API: http://localhost:8080
# Swagger UI: http://localhost:8080/swagger
# PostgreSQL: localhost:5432 (user: postgres, pass: postgres, db: librarymanagement)Auto-migrates the database and seeds demo data on first startup (Orwell, Austen, Hemingway, 3 books, 2 members).
Requires .NET 8 SDK and a running PostgreSQL instance.
# Update connection string in LibraryManagement.API/appsettings.Development.json
dotnet restore
dotnet run --project LibraryManagement.APIdotnet test
# 10 unit tests (handlers + validators, EF InMemory)
# 5 integration tests (full HTTP cycle, WebApplicationFactory + SQLite)LibraryManagement/
├── LibraryManagement.Domain/ Entities, exceptions
├── LibraryManagement.Application/ Commands, queries, DTOs, validators
│ ├── Authors|Books|Genres|Loans|Members|Publishers/
│ │ ├── Commands/ Write operations (Create, Update, Delete)
│ │ ├── Queries/ Read operations (GetById, GetAll)
│ │ ├── Dtos/ Input/output contracts + filters
│ │ └── Validators/ FluentValidation rules
│ └── Common/ IAppDbContext, PagedResult, ValidationBehavior
├── LibraryManagement.Infrastructure/ EF Core DbContext, migrations, configurations
├── LibraryManagement.API/ Controllers, middleware, Program.cs
├── tests/
│ ├── LibraryManagement.UnitTests/
│ └── LibraryManagement.IntegrationTests/
├── docs/ UML diagrams (PlantUML sources)
└── compose.yaml Docker Compose (PostgreSQL + API)
On first startup the database is populated with:
- 3 Authors: George Orwell, Jane Austen, Ernest Hemingway
- 3 Genres: Dystopian, Romance, Adventure
- 2 Publishers: Penguin Books, HarperCollins
- 3 Books: 1984, Pride and Prejudice, The Old Man and the Sea
- 2 Members: Alice Johnson, Bob Smith
CC0 — Public Domain