A reusable .NET 8 Clean Architecture solution intended as a starting point for new
services across the organization. Project folders are named with the project prefix
(POS.*) and live at the repository root — there is no src/ folder.
.
├── POS.Domain/ # Enterprise rules: entities, value objects, domain events, exceptions
├── POS.Application/ # Use cases (CQRS via MediatR), validators, behaviors, interfaces
├── POS.Infrastructure/ # EF Core DbContext, persistence, external services, interceptors
├── POS.WebApi/ # ASP.NET Core entry point: controllers, middleware, Swagger, Serilog
├── POS.Domain.UnitTests/
├── POS.Application.UnitTests/
├── POS.Infrastructure.IntegrationTests/
├── POS.WebApi.FunctionalTests/
├── POS.sln
├── Directory.Build.props # Shared MSBuild props (TFM, nullable, warnings-as-errors)
├── Directory.Packages.props # Central NuGet package version management
├── global.json # Pinned .NET SDK version
├── Dockerfile
├── docker-compose.yml
└── .github/workflows/ # CI + CodeQL
WebApi ─► Application ─► Domain
│ ▲
▼ │
Infrastructure ─┘
Domaindepends on nothing.Applicationdepends only onDomain.Infrastructuredepends onApplicationandDomain.WebApidepends onApplicationandInfrastructure(composition root).
This is the canonical template — when starting a new service, copy this repository and
rename POS to your project's prefix (for example Inventory, Billing):
- Create a new repo from this template (or copy the contents).
- Rename folders, project files, namespaces, and the solution from
POSto<YourProject>(for example using a tool likedotnet-renameor your editor's solution-wide rename). - Update
appsettings.jsonanddocker-compose.ymlconnection strings. - Replace the sample
Productvertical slice with your real aggregates.
# Restore and build
dotnet build POS.sln
# Run the API (uses an in-memory database when no connection string is configured)
dotnet run --project POS.WebApi
# Open Swagger
# https://localhost:5001/swaggerdocker compose up --build
# API: http://localhost:8080
# Health: http://localhost:8080/healthdotnet test POS.slnPOS.Domain.UnitTests— pure unit tests for entities, value objects, events.POS.Application.UnitTests— use-case and validator tests (NSubstitute for fakes).POS.Infrastructure.IntegrationTests— EF Core / persistence tests.POS.WebApi.FunctionalTests— end-to-end HTTP tests viaWebApplicationFactory.
Use the included Product feature as a reference. For each new aggregate:
- Domain — add an entity in
POS.Domain/Entities(inheritBaseAuditableEntityfor auditing, raiseBaseEvents for domain events). - Application — under
POS.Application/<Feature>/:Commands/<Verb><Feature>/—Commandrecord,Validator,Handler.Queries/<Verb><Feature>/—Queryrecord,Validator(if needed),Handler.- DTO classes for read models.
- Infrastructure — add an EF Core configuration in
POS.Infrastructure/Persistence/Configurations/, expose aDbSetonIApplicationDbContextandApplicationDbContext, then add a migration. - WebApi — add a controller under
POS.WebApi/Controllers/. - Tests — add unit tests for the validator/handler and a functional test for the endpoint.
- MediatR pipeline behaviors (registered in
POS.Application/DependencyInjection.cs):UnhandledExceptionBehavior,ValidationBehavior,LoggingBehavior,PerformanceBehavior. - Auditing & domain events — handled by EF Core SaveChanges interceptors in
POS.Infrastructure/Persistence/Interceptors/. - Global error handling —
ExceptionHandlingMiddlewarereturns RFC 7807 ProblemDetails responses. - Logging — Serilog with console sink and request logging.
- Health checks —
/healthendpoint. - OpenAPI — Swagger UI in Development.
TreatWarningsAsErrors=trueis enforced solution-wide viaDirectory.Build.props.- Nullable reference types and implicit usings are enabled everywhere.
- NuGet versions are managed centrally in
Directory.Packages.props— do not putVersion="..."on<PackageReference>items in csproj files. - Test projects are auto-detected by name (
*Tests) inDirectory.Build.props.