Pragmatic Clean Architecture application
- Clean Architecture
- Separation of concerns
- Encapsulation
- Dependency inversion (flow inwards, inner Layers define interfaces, outer layers implement them)
- Explicit dependencies
- Single responsibility principle
- .NET 10
- Dependabot: enable auto-merge for minor updates
- Simple ReadModel
- Message Broker (Rebus+RabbitMQ) for Integration Events
Start the DevContainer
Hit [F5]
or dotnet run --project src/Bookify.Api TODO: not working docker binds to 127.0.0.1?
dotnet test
- Domain Layer Unit Tests
- Application Layer Unit Tests
- Integration Tests (with Testcontainers)
- Functional Tests (e2e, with Testcontainers)
- Architecture Tests (NetArchTest to enforce layer rules, design and naming conventions)
- Database will auto created and seeded with data on application start in
Developmentenvironment - pgAdmin user@user.io, hunter2
- Create new migration:
dotnet ef migrations add -p src/Bookify.Infrastructure -s src/Bookify.Api <Migration_Name>
- Persistence Ignorance
- Domain Driven Design (Bounded Contexts, Entities, ValueObjects, DomainServices, DomainEvents)
- Repositories + Unit of Work (only abstractions)
- Rich Domain Model (Business Logic, Validation in Entities)
- Entities (Object with Identity, private setters, encapsulation)
- Behavior-, State-Transitions-Methods and Commands (raise DomainEvents)
- Value Objects (Immutable, no identity, in C# with record) e.g. Money, Address, DateRange
- Domain Services (Business Logic don't fit to Entity, can call Repositories)
- Domain Events (when something happens in the domain, can be handled by other parts of the system via Mediator Pub/Sub Pattern)
- Folder structure like vertical slices for bounded context -> aggregates.
- Orchestrates the domain
- Business Logic not fitting to Domain
- Use Cases (Driver of the application, taking Domain Model and telling them what to do)
- Application Services (use Repositories, DomainServices)
- CQRS (Command Query Responsibility Segregation), Loose Coupling with Mediator (Request/Response Pattern)
- Cross-Cutting Concerns/Pipeline behaviors (Logging, Validation)
- Folder structure like vertical slices for Use cases (can have subfolder for Commands, Queries, IntegrationEvents)
Pragmatic: use of Dapper for access ReadModels, normaly don't directly use external libs As Alternative see IApartmentReadRepository, but logic is in the Infrastructure Layer
- External concerns (Database, Message Brokers, Identity Providers, File Systems, ...)
- Implementations of abstractions
- Persistence (EF Core and PostgresSQL)
- Conversion of Domain Model to Database Model (ValueObjects as Complex Properties, e.g. User.Email)
- Transactional Outbox Message Pattern for reliable DomainEvents in distributed systems (UnitOfWork when saving Entities)
- Identity Provider: Keycloak
- Entry Point to the system
- WebApi, gRPC, SPA ...
- RESTful API
- Request/Response
- Controllers and Endpoints creates Commands and Queries
- don't expose Commands directly, use DTOs (Request)
- Mediator handles Commands and Queries (loose coupling)
- Dependency setup for all layers
- Middlewares for Cross-Cutting Concerns (Logging)