A multi-tenant construction management backend built with ASP.NET Core (.NET 10) following Clean Architecture and CQRS principles.
- Overview
- Architecture
- Project Structure
- Tech Stack
- Features
- Prerequisites
- Getting Started
- Configuration
- Database Migrations
- API Documentation
- Testing
BuildFlow API is a multi-tenant SaaS backend for managing construction workflows. Each company registers with a unique subdomain and gets an isolated tenant environment. Authentication is handled via JWT access tokens paired with refresh tokens.
The solution follows Clean Architecture β dependencies always point inward.
BuildFlow.API βββΊ BuildFlow.Application βββΊ BuildFlow.Domain
β β β
βΌ βΌ βΌ
BuildFlow.Infrastructure BuildFlow.Persistence BuildFlow.SharedKernel
Every API request is dispatched through MediatR as a Command or Query. Responses are wrapped in a typed Result<T> to enforce explicit error handling throughout all layers.
buildflow-api/
β
βββ src/
β βββ BuildFlow.API/ # HTTP layer β Controllers, Middleware, Swagger, Program.cs
β βββ BuildFlow.Application/ # Use cases β Commands, Queries, Handlers, Validators
β βββ BuildFlow.Contracts/ # Shared request/response DTOs
β βββ BuildFlow.Domain/ # Entities, aggregates, domain rules
β βββ BuildFlow.Infrastructure/ # JWT service, Serilog configuration
β βββ BuildFlow.Persistence/ # EF Core DbContext, Migrations, Identity setup
β βββ BuildFlow.SharedKernel/ # Result<T>, Error, Exceptions, base types
β
βββ tests/
βββ BuildFlow.UnitTests/ # Unit tests β xUnit, Moq, FluentAssertions
βββ BuildFlow.IntegrationTests/ # Integration tests β xUnit, Testcontainers, WebApplicationFactory
| Category | Technology |
|---|---|
| Framework | ASP.NET Core (.NET 10) |
| Language | C# 14 |
| Architecture | Clean Architecture, CQRS |
| Mediator | MediatR |
| Validation | FluentValidation |
| ORM | Entity Framework Core |
| Database | SQL Server / LocalDB |
| Authentication | ASP.NET Core Identity + JWT Bearer |
| Logging | Serilog (Console, File, request logging) |
| API Documentation | Swagger / Swashbuckle |
| API Versioning | Asp.Versioning.Mvc |
| Unit Testing | xUnit, Moq, FluentAssertions, Coverlet |
| Integration Testing | xUnit, Testcontainers (PostgreSQL), FluentAssertions |
- π’ Multi-tenant registration β each company gets a unique subdomain
- π JWT authentication β access token + refresh token flow
- π₯ Role-based authorization β
TenantAdminrole (extensible) - β‘ CQRS + MediatR β clean separation of reads and writes
- π¦ Result pattern β typed
Result<T>andErrorfor consistent responses - π‘οΈ Global exception middleware β centralized error handling
- π API versioning β URL-based (
/api/v1/...) - π Structured logging β Serilog with enriched request logs
- β€οΈ Health checks β
/healthendpoint - π Swagger UI β interactive API docs (development only)
- π CORS β configurable cross-origin policy
- .NET 10 SDK
- SQL Server or SQL Server LocalDB
- Docker Desktop (required for integration tests β Testcontainers)
git clone https://github.com/hysnyasir/buildflow-api.git
cd buildflow-apidotnet restoreUse User Secrets to avoid committing sensitive values:
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=(localdb)\mssqllocaldb;Database=buildflow;Trusted_Connection=True;" --project src/BuildFlow.API
dotnet user-secrets set "Jwt:Key" "YOUR_STRONG_SECRET_KEY_MINIMUM_32_CHARACTERS" --project src/BuildFlow.APIdotnet ef database update `
--project src/BuildFlow.Persistence `
--startup-project src/BuildFlow.APIdotnet run --project src/BuildFlow.APIThe API will be available at:
| URL | Description |
|---|---|
https://localhost:7xxx |
HTTPS |
http://localhost:5xxx |
HTTP |
https://localhost:7xxx/swagger |
Swagger UI (Dev only) |
https://localhost:7xxx/health |
Health check endpoint |
src/BuildFlow.API/appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=buildflow;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Jwt": {
"Key": "REPLACE_WITH_STRONG_SECRET_MIN_32_CHARS_FROM_KEY_VAULT",
"Issuer": "BuildFlow",
"Audience": "BuildFlow",
"ExpiryMinutes": 60,
"RefreshTokenExpiryDays": 7
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
}
}
}
β οΈ Never commit real secrets. Use User Secrets locally and Azure Key Vault / environment variables in production.
# Add a new migration
dotnet ef migrations add <MigrationName> `
--project src/BuildFlow.Persistence `
--startup-project src/BuildFlow.API
# Apply pending migrations
dotnet ef database update `
--project src/BuildFlow.Persistence `
--startup-project src/BuildFlow.API
# Revert last migration
dotnet ef migrations remove `
--project src/BuildFlow.Persistence `
--startup-project src/BuildFlow.APIAll endpoints are versioned under /api/v1/. Swagger UI is available in Development at /swagger.
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/v1/auth/register |
Register a new tenant + admin user | β |
| POST | /api/v1/auth/login |
Authenticate and receive tokens | β |
| POST | /api/v1/auth/refresh |
Refresh access token | β |
{
"companyName": "Acme Construction",
"subdomain": "acme",
"fullName": "John Doe",
"email": "john@acme.com",
"password": "P@ssw0rd123!"
}{
"userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"tenantId": "7cb0a932-1234-5678-abcd-ef1234567890",
"fullName": "John Doe",
"email": "john@acme.com",
"role": "TenantAdmin",
"accessToken": "eyJhbGci...",
"accessTokenExpiry": "2026-08-17T13:00:00Z",
"refreshToken": "dGhpcyBpcyBh...",
"refreshTokenExpiry": "2026-08-24T12:00:00Z"
}dotnet test tests/BuildFlow.UnitTests
β οΈ Requires Docker Desktop running β Testcontainers spins up a PostgreSQL container automatically.
dotnet test tests/BuildFlow.IntegrationTestsdotnet test --collect:"XPlat Code Coverage"This project is private. All rights reserved Β© BuildFlow.