A modern, scalable HR Management System built with .NET 9 following Clean Architecture principles. This API provides comprehensive employee and department management capabilities with JWT authentication and PostgreSQL persistence.
- Employee Management: Create, read, update, and delete employee records with department association
- Department Management: Manage organizational departments and structures
- User Authentication: Secure JWT-based authentication and authorization
- Pagination: Efficient data retrieval with paginated responses
- API Documentation: Interactive OpenAPI/Swagger UI for API exploration
- Structured Logging: Comprehensive logging with Serilog
- Health Checks: Built-in health check endpoints for monitoring
- Docker Support: Containerized deployment with Docker and Docker Compose
- Database Migrations: EF Core migrations with PostgreSQL
This project follows Clean Architecture principles with clear separation of concerns:
src/
βββ HrManagement.Domain/ # Core business logic & entities
β βββ Entities/ # Domain entities (Employee, Department, User)
β βββ Repositories/ # Repository interfaces
β βββ Services/ # Domain services
β βββ Errors/ # Custom error definitions
βββ HrManagement.Application/ # Application use cases & DTOs
β βββ UseCases/ # Application services
β βββ Dtos/ # Data Transfer Objects
β βββ Mapping/ # Entity-to-DTO mappings
β βββ ApplicationModule.cs # DI configuration
βββ HrManagement.Infrastructure/ # External services & database
β βββ Persistence/ # EF Core DbContext & Repositories
β βββ Migrations/ # Database migrations
β βββ Services/ # Infrastructure services
β βββ InfrastructureModule.cs # DI configuration
βββ HrManagement.Presentation/ # API controllers & middleware
β βββ Controllers/ # API endpoints
β βββ Program.cs # Application startup
β βββ Properties/ # Launch settings
βββ HrManagement.Shared/ # Shared utilities & helpers
tests/
βββ HrManagement.Tests/ # Unit tests
Layer Responsibilities:
- Domain: Business rules, entities, and repository contracts
- Application: Use cases, DTOs, and cross-cutting concerns
- Infrastructure: Data access, external services, and EF Core configuration
- Presentation: HTTP controllers, routing, and request/response handling
- .NET 9.0 - Latest .NET runtime
- Entity Framework Core - ORM for database access
- PostgreSQL - Primary database
- JWT Bearer - Authentication & authorization
- Serilog - Structured logging
- Swagger/OpenAPI - API documentation
- Docker & Docker Compose - Containerization
- xUnit - Unit testing framework
- .NET 9.0 SDK or later
- PostgreSQL 15+ (or Docker for containerized setup)
- Docker & Docker Compose (optional, for containerized deployment)
- Visual Studio Code or Visual Studio 2022+ (recommended)
-
Clone the repository:
git clone https://github.com/yourusername/HrManagement.git cd HrManagement -
Build and run with Docker Compose:
docker-compose up --build
-
Access the API:
- API:
http://localhost:5158 - Swagger UI:
http://localhost:5158/api-docs/index.html - PgAdmin:
http://localhost:8080(default: admin@example.com / admin)
- API:
-
Clone the repository:
git clone https://github.com/yourusername/HrManagement.git cd HrManagement -
Set up PostgreSQL:
# Using Docker (single container) docker run --name postgres-hr -e POSTGRES_PASSWORD=admin -e POSTGRES_DB=hrmanagement -p 5432:5432 -d postgres:15 -
Configure connection string: Update
appsettings.jsonor set environment variable:{ "ConnectionStrings": { "DefaultConnection": "Host=localhost;Port=5432;Database=hrmanagement;Username=postgres;Password=admin" } } -
Restore dependencies:
dotnet restore
-
Apply migrations:
dotnet ef database update -p src/HrManagement.Infrastructure
-
Run the application:
dotnet run --project src/HrManagement.Presentation
-
Access the API:
- API:
http://localhost:5158 - Swagger UI:
http://localhost:5158/api-docs/index.html
- API:
POST /api/v1/auth/register- Register new userPOST /api/v1/auth/login- Authenticate user (returns JWT token)
GET /api/v1/employees- Get all employees (with pagination)GET /api/v1/employees/{id}- Get employee by IDPOST /api/v1/employees- Create new employeePUT /api/v1/employees/{id}- Update employeeDELETE /api/v1/employees/{id}- Delete employee
GET /api/v1/departments- Get all departmentsGET /api/v1/departments/{id}- Get department by IDPOST /api/v1/departments- Create new departmentPUT /api/v1/departments/{id}- Update departmentDELETE /api/v1/departments/{id}- Delete department
GET /health- Service health status
Detailed API documentation is available at http://localhost:5158/api-docs/index.html when the application is running.
The API uses JWT (JSON Web Tokens) for authentication:
-
Obtain a token:
curl -X POST http://localhost:5158/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"user","password":"password"}'
-
Use the token in requests:
curl -H "Authorization: Bearer {token}" \ http://localhost:5158/api/v1/employees
Employee
Id(Guid) - Primary keyName(string) - Employee nameEmail(string) - Email addressPosition(string) - Job positionDepartmentId(Guid) - Foreign key to DepartmentCreatedAt(DateTime) - Creation timestamp
Department
Id(Guid) - Primary keyName(string) - Department nameDescription(string) - Department descriptionCreatedAt(DateTime) - Creation timestamp
User
Id(Guid) - Primary keyUsername(string) - Login usernameEmail(string) - Email addressPasswordHash(string) - Hashed passwordCreatedAt(DateTime) - Creation timestamp
Run unit tests:
dotnet testRun tests with coverage:
dotnet test /p:CollectCoverageMetrics=trueappsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Port=5432;Database=hrmanagement;Username=postgres;Password=admin"
},
"Jwt": {
"Key": "your-secret-key-here-minimum-32-chars",
"Issuer": "hr-management-api",
"Audience": "hr-management-client",
"ExpirationMinutes": 60
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}Store sensitive configuration locally:
dotnet user-secrets init -p src/HrManagement.Presentation
dotnet user-secrets set "Jwt:Key" "your-secure-secret-key-here"- api - .NET 9 web API (port 5158)
- db - PostgreSQL 15 database (port 5432)
- pgadmin - PostgreSQL administration (port 8080)
docker build -t hrmanagement:latest .docker run -p 5158:5158 \
-e ConnectionStrings__DefaultConnection="Host=db;Port=5432;Database=hrmanagement;Username=postgres;Password=admin" \
hrmanagement:latestThe application uses Serilog for structured logging:
- Console output (development)
- Rolling file sinks (production)
- Request/response middleware logging
- Exception logging
Logs can be configured in appsettings.json under the Serilog section.
Create a new migration:
dotnet ef migrations add MigrationName -p src/HrManagement.InfrastructureApply migrations:
dotnet ef database update -p src/HrManagement.InfrastructureRemove last migration:
dotnet ef migrations remove -p src/HrManagement.Infrastructure- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow C# naming conventions (PascalCase for public members)
- Use dependency injection for loose coupling
- Write unit tests for new features
- Keep methods focused and single-responsibility
- Add XML documentation comments to public methods
- Domain: Pure business logic, no external dependencies
- Application: Orchestration layer, DTOs, and use cases
- Infrastructure: EF Core context, repositories, migrations
- Presentation: Controllers, middleware, HTTP concerns
- Tests: Unit tests, integration tests, test fixtures
Problem: Connection refused to PostgreSQL
- Solution: Ensure PostgreSQL is running and credentials match
appsettings.json - Docker Fix:
docker-compose up db(restart database service)
Problem: dotnet ef database update fails
- Solution: Delete problematic migration and recreate:
dotnet ef migrations remove -p src/HrManagement.Infrastructure dotnet ef migrations add NewMigrationName -p src/HrManagement.Infrastructure
Problem: 401 Unauthorized on protected endpoints
- Solution: Verify token is passed in Authorization header as
Bearer {token} - Check token hasn't expired (default: 60 minutes)
- Verify
Jwt:Keymatches between token generation and validation
Problem: Port already in use
- Solution: Change ports in
docker-compose.ymlor stop conflicting services:docker-compose down
This project is licensed under the MIT License - see the LICENSE file for details.
- Emmanuel Francois - Initial work
For issues, questions, or suggestions:
- Open an issue on GitHub
- Contact: emmanuelnoc@gmail.com
- Documentation: Wiki
- .NET Documentation
- Entity Framework Core
- Clean Architecture
- JWT Introduction
- PostgreSQL Documentation
Last Updated: 2026
Status: Active Development