Clean Architecture reference implementation in .NET 10, designed to demonstrate separation of concerns, dependency inversion, and a practical Minimal API request flow.
The solution in src/AmitBytes.CleanArchitecture.slnx contains these projects:
Domain- core business model (Customers, value objects, domain exceptions).Application- use cases/services and abstraction contracts (repository/service interfaces, DI installers).Infrastructure- implementation details (repository implementations, entity configuration bootstrap).Contract- API-facing DTO contracts.AmitBytes.CleanArchitecture.Common- shared primitives (Result,Error, enum helpers).Api/MinWebApi- main composition root and working Minimal API host.Api/WebApi- secondary API host scaffold.
Dependencies point inward toward core business logic (compile-time project references):
Api/MinWebApi -> Application, InfrastructureApi/WebApi -> Application, ContractInfrastructure -> Application, DomainApplication -> Domain, Contract, AmitBytes.CleanArchitecture.CommonDomainhas no project references
flowchart TB
subgraph apiLayer [API Layer]
minApi[Api MinWebApi]
webApi[Api WebApi]
end
subgraph infraLayer [Infrastructure Layer]
infrastructure[Infrastructure]
end
subgraph appLayer [Application Layer]
applicationContracts[Application ContractsAndServices]
contract[Contract DTOs]
common[Common ResultAndError]
end
subgraph domainLayer [Domain Layer]
domain[Domain EntitiesAndValueObjects]
end
minApi -->|"references"| applicationContracts
minApi -->|"references"| infrastructure
webApi -->|"references"| applicationContracts
webApi -->|"references"| contract
infrastructure -->|"references (uses app interfaces/contracts)"| applicationContracts
infrastructure -->|"references"| domain
applicationContracts -->|"references"| domain
applicationContracts -->|"references"| contract
applicationContracts -->|"references"| common
The primary runnable flow is in Api/MinWebApi, wired from Program.cs through endpoint and service installers.
flowchart LR
client[DeveloperClient] -->|"GET /api/customers/{id}"| minApi[MinWebApi]
minApi --> endpointInstaller[CustomerEndpointsRouteInstaller]
endpointInstaller --> appService[ICustomerService CustomerService]
appService --> repoContract[ICustomerRepository]
repoContract --> infraRepo[CustomerRepository]
infraRepo --> resultDomain[Result Customer]
resultDomain --> appService
appService --> responseDto[CustomerRead Contract]
responseDto --> client
src/Api/MinWebApi/Program.cs is the composition root:
AddApplicationServices(...)scans and registersIServiceInstallerimplementations fromApplication.AddInfrastructureServices(...)scans and registersIServiceInstallerimplementations fromInfrastructure.UseInfrastructureEntityConfiguration()initializes and validates entity mappings at startup.UseEndpointRoutesInstaller<Program>()discoversIEndpointsRouteInstallerimplementations and maps routes.
This allows new features to be added with minimal changes to Program.cs: place new installers/endpoints in the expected folders and they are picked up automatically by reflection.
src/
├── AmitBytes.CleanArchitecture.Common/
├── Domain/
│ ├── Customers/
│ ├── ValueObjects/
│ └── Exceptions/
├── Application/
│ ├── Interfaces/
│ ├── Services/
│ ├── ServiceInstallers/
│ └── Extensions/
├── Infrastructure/
│ ├── Customers/
│ ├── Common/
│ ├── ServiceInstaller/
│ └── Extensions/
├── Contract/
│ └── Customers/
└── Api/
├── MinWebApi/
└── WebApi/
- .NET 10 SDK
- Git
dotnet restore src/AmitBytes.CleanArchitecture.slnx
dotnet build src/AmitBytes.CleanArchitecture.slnx
dotnet run --project src/Api/MinWebApi/AmitBytes.CleanArchitecture.MinWebApi.csprojMinWebApi launch profiles:
https://localhost:7110http://localhost:5266
WebApi launch profiles:
https://localhost:7102http://localhost:5267
After starting MinWebApi, call:
GET https://localhost:7110/api/weatherforecastGET https://localhost:7110/api/customers/1(requires aCustomerrow with that id in your database)
You can also use src/Api/MinWebApi/AmitBytes.CleanArchitecture.MinWebApi.http.
- Run
MinWebApiand verifyweatherforecastendpoint works. - Open
src/Api/MinWebApi/Program.csto understand startup wiring. - Follow endpoint discovery via
UseEndpointRoutesInstallertoRouteEndpoints. - Trace
CustomerEndpointsRouteInstaller -> ICustomerService -> CustomerRepository. - Review
ResultandErrorinAmitBytes.CleanArchitecture.Commonfor success/failure flow.
- Domain: add entity/value objects/exceptions under
src/Domain. - Application:
- define repository interface under
src/Application/Interfaces/Persistence/Repository. - define service interface/implementation under
src/Application/Services. - register service in an
IServiceInstallerundersrc/Application/ServiceInstallers.
- define repository interface under
- Infrastructure:
- implement repository under
src/Infrastructure/<Feature>/Persistence. - register implementation in an
IServiceInstallerundersrc/Infrastructure/ServiceInstaller. - add entity configuration under
src/Infrastructure/<Feature>/Configurationif needed.
- implement repository under
- API (MinWebApi):
- add an endpoint installer implementing
IEndpointsRouteInstallerundersrc/Api/MinWebApi/RouteEndpoints. - use
GlobalRoutePrefixfrom configuration when building route groups.
- add an endpoint installer implementing
- Contract:
- add/extend DTO records under
src/Contractand map in application services.
- add/extend DTO records under
- Prefer returning
Result/Result<T>from service and repository boundaries. - Keep
Domainfree from infrastructure concerns. - Keep dependency direction inward (outer layers can depend on inner layers, not vice versa).
- Use installers for DI rather than hard-coding registrations in
Program.cs. - Keep route mapping in endpoint installer classes, not directly in startup.
These are the implementation standards already followed in this repository and should be maintained for all upcoming changes:
- Clean dependency direction: preserve inward project references (
API -> Application -> Domain, withInfrastructureimplementingApplicationcontracts). - Separation of concerns by layer: keep business rules in
Domain, orchestration inApplication, and technical implementations inInfrastructure. - Contract-first boundaries: expose API/application payloads through
ContractDTOs instead of leaking domain models directly. - Consistent success/failure handling: use
ResultandErrorpatterns to avoid exception-driven control flow across service boundaries. - Composition root discipline: keep startup wiring in
Program.csminimal and push registrations to installer classes. - Convention-based extensibility: implement
IServiceInstallerandIEndpointsRouteInstallerso new services and routes are auto-discovered. - Endpoint thinness: keep endpoint handlers lightweight; delegate use-case logic to application services.
- Async-first I/O boundaries: repository and service operations are asynchronous to support scalable API handling.
Before raising a PR, ensure:
- New feature code is placed in the correct layer and does not violate dependency direction.
- New services/repositories are registered via installer interfaces, not ad-hoc startup code.
- Endpoint logic remains orchestration-only (no embedded persistence/business complexity).
- Returned responses use contract types and follow the
Result-based error handling style. - Build and smoke tests pass locally for changed endpoints.
- There are currently no test projects in this repository.
- Use build + API smoke tests as the current validation path:
dotnet build src/AmitBytes.CleanArchitecture.slnx
dotnet run --project src/Api/MinWebApi/AmitBytes.CleanArchitecture.MinWebApi.csprojAs the next step, add dedicated test projects (Domain/Application/Infrastructure/API) and wire dotnet test into CI.
- Local SQL Server must contain a
Customertable matching entity configuration; otherwise customer reads will fail at runtime. Schema changes will be introduced later via a migration-based workflow (not ad-hoc SQL scripts in this repository). - Several generic repository methods are placeholders.
- API key authentication handler exists but registration is commented out in
Program.cs.
- Introduce a migration workflow for schema changes and environments.
- Add automated tests per layer and enforce them in CI.
- Expand endpoint coverage and error-path handling with consistent
Resultusage.
- Fork the repository.
- Create a branch:
git checkout -b feature/your-change. - Implement and verify changes locally.
- Open a pull request with context and test notes.
This project is licensed under the MIT License. See LICENSE.