A Full-stack portfolio project demonstrating Clean Architecture, CQRS, multi-tenancy, containerisation, and CI/CD.
Build a scalable, maintainable platform that showcases:
- Backend engineering with .NET 9 and Clean Architecture
- Frontend engineering with Angular 17 and Signals
- Cloud-native patterns (multi-tenancy, audit logging, blob storage)
- Infrastructure as code (Docker, GitHub Actions)
- Test-driven quality (43 unit tests)
| Layer | Technology |
|---|---|
| Runtime | .NET 9 Web API |
| Architecture | Clean Architecture (Domain → Application → Infrastructure → API) |
| CQRS | MediatR 14 |
| ORM | Entity Framework Core 9 |
| Database | SQL Server / Azure SQL |
| Validation | FluentValidation 12 |
| Mapping | AutoMapper 16 |
| Auth | JWT Bearer + RBAC (Admin, HR, Manager) |
| Audit Logging | Azure Cosmos DB |
| File Storage | Azure Blob Storage |
| Logging | Serilog |
| API Docs | Swagger / Swashbuckle |
| Layer | Technology |
|---|---|
| Framework | Angular 17 (standalone components) |
| State | Angular Signals |
| HTTP | RxJS + custom ApiService |
| UI | Angular Material |
| Auth | JWT interceptor + role-based guards |
| Concern | Technology |
|---|---|
| Containerisation | Docker + Docker Compose |
| CI | GitHub Actions (PR validation) |
| Cloud Target | Azure App Service + Azure Static Web Apps |
HRLite/
├── backend/
│ ├── src/
│ │ ├── HRLite.Domain/ # Entities, Enums, Interfaces
│ │ ├── HRLite.Application/ # CQRS Handlers, DTOs, Validators, Mappings
│ │ ├── HRLite.Infrastructure/ # EF Core, Repos, JWT, Cosmos, Blob
│ │ └── HRLite.API/ # Controllers, Middleware, Program.cs
│ └── tests/
│ ├── HRLite.Domain.Tests/ # Entity unit tests (xUnit)
│ └── HRLite.Application.Tests/ # Handler & validator tests (xUnit + NSubstitute)
├── frontend/
│ └── hrlite-ui/ # Angular 17 app
├── docker-compose.yml # SQL Server + API + Frontend
└── .github/workflows/ci.yml # PR validation pipeline
- Clean Architecture — strict layer boundaries; Domain has zero external dependencies
- CQRS via MediatR — every use case is an isolated command or query handler
- Repository pattern —
IRepository<T>abstracts all data access - Pipeline behaviours — logging and validation run automatically before every handler
- Multi-tenancy —
TenantIdon every entity, enforced via EF Core global query filters - Soft delete —
IsDeletedflag + global query filter; no hard deletes - JWT + RBAC — three roles (Admin, HR, Manager) enforced at controller and route level
| Module | Capabilities |
|---|---|
| Authentication | Login, JWT token, role-based access |
| Employees | Create, read, update, soft delete; auto-generated employee code |
| Departments | CRUD with employee count |
| Leave Management | Apply, approve/reject, track status |
| Attendance | Mark daily attendance, duplicate prevention |
| Documents | Upload to Azure Blob, store metadata in SQL |
| Tool | Version |
|---|---|
| Docker Desktop | Latest (WSL2 backend on Windows) |
| .NET SDK | 9.0 (for local development only) |
| Node.js | 20+ (for local development only) |
Docker is the only requirement to run the full stack.
git clone https://github.com/YOUR_USERNAME/HRLite.git
cd HRLitedocker-compose up --buildThis will:
- Start SQL Server and wait for it to be healthy
- Start the .NET API — runs EF migrations and seeds demo data automatically
- Start the Angular frontend served via nginx
| Service | URL |
|---|---|
| Frontend | http://localhost:4200 |
| API Swagger | http://localhost:5217/swagger |
| SQL Server | localhost,1433 |
Tenant:
tenant-demo
| Role | Password | |
|---|---|---|
| Admin | admin@hrlite.com | Admin@123 |
| HR | hr@hrlite.com | Hr@12345 |
| Manager | manager@hrlite.com | Manager@123 |
# Run in background
docker-compose up --build -d
# View API logs
docker-compose logs -f api
# Stop everything
docker-compose down
# Wipe database and start fresh
docker-compose down -vcd backend
dotnet restore
dotnet run --project src/HRLite.APIUpdate
appsettings.Development.jsonwith a valid SQL Server connection string before running.
cd frontend/hrlite-ui
npm install
ng serveApp will be available at http://localhost:4200.
cd backend
dotnet test --verbosity normal| Project | Tests | Coverage |
|---|---|---|
| HRLite.Domain.Tests | 14 | Entity defaults, invariants |
| HRLite.Application.Tests | 29 | Command handlers, validators |
Sensitive values are passed as environment variables in docker-compose.yml. Before deploying to production, replace all placeholder values:
| Variable | Description |
|---|---|
ConnectionStrings__DefaultConnection |
SQL Server connection string |
Jwt__Key |
JWT signing key (min 32 chars) |
CosmosDb__ConnectionString |
Azure Cosmos DB connection string |
AzureBlob__ConnectionString |
Azure Blob Storage connection string |
Important: The credentials in
appsettings.Development.jsonanddocker-compose.ymlare for local development only. Never use them in production. Use Azure Key Vault or environment-specific secrets management instead.
Cosmos DB and Azure Blob Storage are optional for local development. If connection strings contain placeholder values (REPLACE), the application automatically falls back to a no-op audit logger and will skip blob operations.
A GitHub Actions workflow runs on every pull request to main:
| Job | What it does |
|---|---|
backend |
Restore → Build → Test |
frontend |
npm ci → ng build --production |
docker |
docker compose build (image validation) |
- Azure deployment (App Service + Static Web Apps)
- OpenTelemetry tracing
- Redis caching for frequently read data
- Refresh token support
- Pagination filters on all list endpoints