This repository is a small proof of concept (POC) demonstrating how to apply several architectural principles and patterns together: Clean Architecture, CQRS, SOLID and Domain-Driven Design (DDD). The goal is to provide a simple, reproducible structure that shows clear separation of responsibilities and how layers interact.
-
Clean Architecture
- The solution is organized into logical layers: Api (presentation), Application (use cases / handlers), Domain (entities, aggregates, domain events, repository interfaces), Infrastructure (EF Core).
- Dependency directions follow the rule: inner layers (Domain/Application) are not coupled to outer layers (Infrastructure). Infrastructure references Application/Domain to implement interfaces.
- Dependency directions follow the rule: Domain does not reference any other layer. Application only references Domain. Infractructure references Application (and Domain). Presentation references Application (and Domain) to implement interfaces.
- The API assembly in this solution acts as the presentation layer and additionally contains the Program/host entry point (Program.cs). This is intentional is acceptable provided the following is respected:
- The presentation layer (API) must not contain infrastructure logic. It should only map HTTP requests to Commands/Queries and delegate to the Application layer.
- It is acceptable for Program.cs (composition root) to reference Infrastructure artifacts to register services (DbContexts, repositories, messaging). This single responsibility (composition only) should not break the Clean Architecture rule: presentation code (endpoints) must not directly reference infrastructure types or implementation details.
-
CQRS
- Commands (mutations) and Queries (reads) are separated. Handlers live under
src/*/Application/Commandsandsrc/*/Application/Queries. - HTTP endpoints are thin and send Commands/Queries to the message bus (Wolverine) instead of embedding business logic.
- Commands (mutations) and Queries (reads) are separated. Handlers live under
-
Domain-Driven Design (DDD)
- Aggregates (e.g., Fund, Currency) and Value Objects (e.g., Isin) encapsulate business rules and invariants.
- Domain events are used (e.g., FundNavUpdatedDomainEvent) and collected from entities via Entity.DomainEvents.
-
SOLID
- Single Responsibility: handlers, repositories, entities and endpoints have distinct responsibilities.
- Dependency Inversion: repository interfaces are defined in the domain and implemented in Infrastructure; the implementations are injected via DI at composition time.
-
Wolverine (in-memory mediator + message broker)
- Wolverine is configured (see
src/Common/AspNetCore/Configuration/WolverineExtensions.cs) to:- use RabbitMQ as message boker;
- enable durable outbox/inbox to ensure message delivery.
- integrate EF Core and automatically apply transactions to command handlers;
- publish DomainEvents extracted from EF-tracked entities;
- Practical consequence: handlers add/modify entities through repositories but should not call
SaveChangesdirectly — Wolverine applies the commit (SaveChangesAsync) within its pipeline.
- Wolverine is configured (see