dotnet-cqrs/docs/ARCHITECTURE.md
2026-03-08 14:02:19 -04:00

179 lines
6.0 KiB
Markdown

# Architecture
> Svrnty.CQRS is a modular CQRS/event-sourcing framework for .NET 10, organized as 18 NuGet packages with clear separation of concerns.
## Package Dependency Graph
```
Svrnty.CQRS.Abstractions
(ICommandHandler, IQueryHandler)
|
+-----------------+-----------------+
| |
Svrnty.CQRS Svrnty.CQRS.FluentValidation
(Discovery, Registration, (AbstractValidator<T> binding)
CqrsBuilder, DI) depends on: Abstractions, Core
|
+------------+------------+---------------------------+
| | | |
MinimalApi Grpc DynamicQuery Sagas
(HTTP REST) (gRPC) (Filtering, (Orchestrator,
| Sorting, Paging) Compensation)
| | |
Grpc.Abstractions DQ.Abstractions Sagas.Abstractions
(GrpcIgnore attr) (IQueryableProvider) (ISaga, ISagaBuilder,
| | | ISagaOrchestrator)
Grpc.Generators DQ.MinimalApi | |
(Source gen, (HTTP endpoints | Sagas.RabbitMQ
.proto gen) for DQ) | (RabbitMQ transport)
|
DQ.EntityFramework
(EF Core provider)
Events.Abstractions Notifications.Abstractions
(IDomainEvent, (INotificationPublisher,
IDomainEventPublisher) StreamingNotificationAttribute)
| |
Events.RabbitMQ Notifications.Grpc
(RabbitMQ transport) (gRPC streaming)
```
## Dependency Matrix
| Package | Depends On (internal) |
|---|---|
| `Svrnty.CQRS.Abstractions` | _(none)_ |
| `Svrnty.CQRS` | Abstractions |
| `Svrnty.CQRS.MinimalApi` | Abstractions, Core |
| `Svrnty.CQRS.Grpc` | Core |
| `Svrnty.CQRS.Grpc.Abstractions` | _(none)_ |
| `Svrnty.CQRS.Grpc.Generators` | _(none, Roslyn source gen)_ |
| `Svrnty.CQRS.FluentValidation` | Abstractions, Core |
| `Svrnty.CQRS.DynamicQuery.Abstractions` | _(none)_ |
| `Svrnty.CQRS.DynamicQuery` | DynamicQuery.Abstractions, Core |
| `Svrnty.CQRS.DynamicQuery.MinimalApi` | Abstractions, DynamicQuery.Abstractions, DynamicQuery |
| `Svrnty.CQRS.DynamicQuery.EntityFramework` | DynamicQuery |
| `Svrnty.CQRS.Events.Abstractions` | _(none)_ |
| `Svrnty.CQRS.Events.RabbitMQ` | Events.Abstractions |
| `Svrnty.CQRS.Sagas.Abstractions` | _(none)_ |
| `Svrnty.CQRS.Sagas` | Core, Sagas.Abstractions |
| `Svrnty.CQRS.Sagas.RabbitMQ` | Sagas |
| `Svrnty.CQRS.Notifications.Abstractions` | _(none)_ |
| `Svrnty.CQRS.Notifications.Grpc` | Notifications.Abstractions |
## CQRS Data Flow
### Command Flow
```
Client Request
|
v
[MinimalApi POST /api/command/{name}] or [gRPC CommandService/{name}]
|
v
FluentValidation (if validator registered)
|
|-- Validation fails --> RFC 7807 ProblemDetails (HTTP) / Google Rich Error (gRPC)
|
v
ICommandHandler<TCommand, TResult>.HandleAsync(command, ct)
|
v
Command Result (or void)
|
+--> (optional) IDomainEventPublisher.PublishAsync(event)
+--> (optional) INotificationPublisher.PublishAsync(notification)
```
### Query Flow
```
Client Request
|
v
[MinimalApi POST /api/query/{name}] or [gRPC QueryService/{name}]
|
v
IQueryHandler<TQuery, TResult>.HandleAsync(query, ct)
|
v
Query Result
```
### Dynamic Query Flow
```
Client Request (with filters, sorts, pagination)
|
v
[MinimalApi POST /api/dynamic-query/{entity}]
|
v
IQueryableProvider<TSource>.GetQueryableAsync(query, ct)
|
v
PoweredSoft.DynamicQuery engine (applies filters, sorts, groups, aggregates)
|
v
IAlterQueryableService (optional interception)
|
v
Paged/Grouped result set
```
### Saga Flow
```
ISagaOrchestrator.StartAsync<TSaga, TData>(data)
|
v
ISaga<TData>.Configure(builder) -- defines steps
|
v
Step 1: Execute --> Step 2: Execute --> Step 3: Execute --> Completed
| | |
| | +-- fails -->
| | |
| +-- compensate <-----------------+
| |
+-- compensate <-----------------+
|
v
Compensated (rolled back)
```
## Separation of Concerns
The framework follows a layered architecture:
1. **Abstractions layer** (4 packages) -- Pure interfaces and marker types with zero dependencies. Can be referenced by any project without pulling in implementation details.
- `Svrnty.CQRS.Abstractions`
- `Svrnty.CQRS.DynamicQuery.Abstractions`
- `Svrnty.CQRS.Events.Abstractions`
- `Svrnty.CQRS.Sagas.Abstractions`
- `Svrnty.CQRS.Grpc.Abstractions`
- `Svrnty.CQRS.Notifications.Abstractions`
2. **Core layer** (1 package) -- Handler discovery, DI registration, and the `CqrsBuilder` fluent API.
- `Svrnty.CQRS`
3. **Transport layer** (4 packages) -- Maps commands/queries to HTTP or gRPC endpoints.
- `Svrnty.CQRS.MinimalApi`
- `Svrnty.CQRS.Grpc`
- `Svrnty.CQRS.Grpc.Generators`
- `Svrnty.CQRS.DynamicQuery.MinimalApi`
4. **Feature layer** (4 packages) -- Optional capabilities that can be composed in.
- `Svrnty.CQRS.FluentValidation`
- `Svrnty.CQRS.DynamicQuery`
- `Svrnty.CQRS.DynamicQuery.EntityFramework`
- `Svrnty.CQRS.Sagas`
5. **Infrastructure layer** (3 packages) -- Concrete transport bindings for messaging and streaming.
- `Svrnty.CQRS.Events.RabbitMQ`
- `Svrnty.CQRS.Sagas.RabbitMQ`
- `Svrnty.CQRS.Notifications.Grpc`
This layering ensures that application code depends only on abstractions, while transport and infrastructure concerns remain pluggable.