Backend for the "Vaccination Manager" technical challenge. Lightweight, testable API implemented with .NET 8, EF Core and a clear layered structure (API -> Application -> Domain -> Infrastructure).
- Frontend Repository: Vaccination Manager Frontend
- Frontend Deploy Vercel: Vaccination Manager Frontend Deploy
- Swagger Documentation Render: Vaccination Manager Swagger
- Overview
- Tech stack
- Features
- Run locally
- Docker Compose
- Project structure
- API routes and examples
- Validation and error handling
- Persistence and Fluent API
- Authentication and authorization
- Health check and logging
- Architectural decisions
- Possible Improvements (Roadmap)
- Author
Manage persons, vaccines and vaccination records. The API follows RESTful conventions, returns JSON, and exposes Swagger UI for interactive documentation.
Instead of just building a simple CRUD, I focused on architectural quality. I used Clean Architecture to keep the core logic testable and decoupled. I also implemented a custom JWT & Refresh Token authentication flow (using HttpOnly cookies) to handle security properly without relying on heavy default templates.
- .NET 8 (Web API)
- Entity Framework Core (EF Core)
- Custom JWT Authentication (Stateless)
- PostgreSQL (development and production)
- BCrypt.Net (Password Hashing)
- Mapster (DTO mapping)
- Swashbuckle / Swagger (API docs)
- Docker / Docker Compose (containerized development)
- xUnit (unit tests for domain)
- CRUD for Person, Vaccine and VaccinationRecord.
- Pagination support for list endpoints.
- DTOs for API responses (Separation of concerns).
- Swagger with XML comments enabled.
- Custom JWT Authentication:
- Stateless architecture (no ASP.NET Identity tables).
- Refresh Token implementation using HttpOnly Cookies (XSS protection).
- Secure Password Hashing with BCrypt.
- Database Seeding: Automatic seeding of Admin user and initial vaccines on startup (Development mode).
- Postgres database in Docker for consistent local development.
The database model respects the separation between Auth (Access) and Business Domain (Vaccination).
erDiagram
User ||--o{ RefreshToken : owns
Person ||--o{ VaccinationRecord : has
Vaccine ||--o{ VaccinationRecord : applied_in
User {
uuid Id
string Email
string PasswordHash
}
RefreshToken {
uuid Id
string TokenHash
date Expires
bool IsRevoked
}
Person {
uuid Id
string Name
}
Vaccine {
uuid Id
string Name
}
VaccinationRecord {
uuid Id
date AppliedAt
int Dose
uuid PersonId
uuid VaccineId
}
- .NET 8 SDK
- Git
- Docker and Docker Compose
- Clone the repository:
git clone https://github.com/Ghitado/vaccination-manager-backend.git
cd vaccination-manager-backend
dotnet restore
dotnet build2.Create a .env file in the root directory (or rename .env.example) to configure database credentials and JWT Key:
DB_USER=postgres
DB_PASSWORD=ExamplePassword123!
DB_NAME=ExampleDb
JWT_KEY=ALongRandomStringWithMoreThan64CharactersHereForSecurityFromJWT512BitsTo execute the unit tests for the Domain layer:
dotnet testBuild and run the entire stack (API + Postgres) with a single command:
docker compose up --buildThe API will be available at http://localhost:5001/swagger.
src/VaccinationManager.Api/— Controllers, Program.cs, Swagger, Docker setup.src/VaccinationManager.Application/— Use Cases, DTOs, Interfaces (Ports).src/VaccinationManager.Domain/— Entities, Repository Interfaces, Domain Exceptions.src/VaccinationManager.Infrastructure/— EF Core Implementation, Migrations, Security Implementations (JWT/BCrypt).tests/— Unit tests.
Base URL: https://localhost:5001. All endpoints use JSON.
Auth
- POST
/api/auth/register— Register a new user. - POST
/api/auth/login— Authenticate user. - POST
/api/auth/refresh— Renew Access Token.
Persons
- POST
/api/person— Create person. - GET
/api/person?pageNumber=1&pageSize=10— List persons (paginated). - GET
/api/person/{id}— Get person by id (it will also retrive the vaccination records). - DELETE
/api/person/{id}— Delete person by id.
Vaccines
- POST
/api/vaccine— Create vaccine- Validation: Prevents future dates and ensures IDs exist.
- GET
/api/vaccine?pageNumber=1&pageSize=10— List vaccines (paginated).
VaccinationRecords
- POST
/api/vaccinationrecord— Create vaccination record. - DELETE
/api/vaccinationrecord/{id}— Delete vaccination record by id.
- DTO Validation: Uses Data Annotations (
[Required],[EmailAddress],[StringLength]) for immediate feedback. - Domain Validation: Entities protect their invariants (for example,
Dose > 0). - Global Exception Handler: Middleware translates exceptions into proper HTTP Status Codes:
DomainException->400 Bad RequestUnauthorizedAccessException->401 UnauthorizedKeyNotFoundException->404 Not Found
- PostgreSQL is the database provider.
- Fluent API (
IEntityTypeConfiguration) is used to configure schema, indexes, and relationships, keeping the Domain entities clean of persistence attributes. - UTC Enforcement: Dates are converted to UTC before saving to ensure compatibility with Postgres
timestamp with time zone.
- Custom Implementation: Instead of using the heavy ASP.NET Core Identity, a lightweight custom authentication system was built following Clean Architecture principles.
- JWT (Access Token): Short-lived token for authorization.
- Refresh Token: Long-lived, opaque token stored in the database (hashed) and sent via HttpOnly Secure Cookie to prevent XSS attacks.
- BCrypt: Used for secure password hashing.
- Health Check: Exposes
GET /health(public) for uptime monitoring and Docker probes. - Logging: Built-in
ILoggeris used with structured logging in Controllers to track operations (for example, "User registered with ID: {Id}").
- Clean Architecture: Strict separation of concerns (Domain, Application, Infrastructure, API). Dependencies point inwards.
- Custom Auth vs Identity: Chose to implement a custom JWT flow to reduce complexity/overhead and have full control over the Refresh Token mechanism.
- Docker Compose: Ensures the development environment (API + DB) is identical across machines.
- Repository Pattern: Abstraction over EF Core to allow easier testing and decoupling.
- Health Check Strategy: A specific, public
/healthendpoint was implemented to allow the frontend to "ping" the server and detect when the free-tier hosting is waking up, improving user experience during cold starts.
- Dose Modeling: Currently, the system uses an integer to represent doses (1, 2, 3...). In a real-world public health scenario, I would refactor the
VaccinationRecordentity to include anEnumfor Dose Type (Primary, Booster, Single) and Strategy (Routine, Campaign), enabling precise epidemiological reporting.
Developed by Thiago de Melo Mota as part of a technical challenge.
