dotnet-cqrs/docs/architecture/README.md

322 lines
9.3 KiB
Markdown

# Architecture
Understand the design principles, patterns, and extensibility of Svrnty.CQRS.
## Overview
Svrnty.CQRS is built on solid architectural principles that make it:
-**Metadata-driven** - Runtime discovery through compile-time registration
-**Modular** - Clear separation between abstractions and implementations
-**Extensible** - Multiple extension points for customization
-**Convention-based** - Minimal configuration with sensible defaults
-**Type-safe** - Compile-time type checking with runtime flexibility
## Architecture Topics
### [CQRS Pattern](cqrs-pattern.md)
Deep dive into the Command Query Responsibility Segregation pattern:
- Separation of reads and writes
- Benefits and trade-offs
- When to use CQRS
- Common anti-patterns
- Implementation patterns
### [Metadata Discovery](metadata-discovery.md)
How Svrnty.CQRS uses metadata for automatic endpoint generation:
- Metadata registration pattern
- Discovery services (ICommandDiscovery, IQueryDiscovery)
- Runtime enumeration
- Endpoint generation process
- Type safety with generics
### [Modular Solution Structure](modular-solution-structure.md)
Best practices for organizing your solution into layers:
- Multi-project solution structure
- Api → CQRS → Domain → DAL dependencies
- Separation of concerns
- Project references
- Real-world example
### [Dependency Injection](dependency-injection.md)
DI patterns and handler registration:
- Service registration patterns
- Handler lifetime management
- Scoped vs Transient vs Singleton
- Constructor injection
- Service resolution
### [Extensibility Points](extensibility-points.md)
Framework extension mechanisms:
- Custom authorization services
- Query alteration services
- Dynamic query interceptors
- Custom attributes
- Middleware integration
## Key Architectural Concepts
### 1. Abstractions vs Implementations
Svrnty.CQRS separates interfaces from implementations:
```
Svrnty.CQRS.Abstractions (interfaces only)
↓ depends on
Svrnty.CQRS (core implementation)
↓ depends on
Svrnty.CQRS.MinimalApi (HTTP integration)
Svrnty.CQRS.Grpc (gRPC integration)
```
**Benefits:**
- Consumer projects reference only abstractions
- Minimal dependencies
- Easy to swap implementations
- Clear contracts
### 2. Metadata-Driven Discovery
Instead of scanning assemblies at runtime, Svrnty.CQRS uses explicit metadata:
```csharp
// Registration creates metadata
services.AddCommand<CreateUserCommand, int, CreateUserCommandHandler>();
// Metadata stored as singleton
services.AddSingleton<ICommandMeta>(new CommandMeta<CreateUserCommand, int, CreateUserCommandHandler>());
// Discovery queries metadata
public class CommandDiscovery : ICommandDiscovery
{
private readonly IEnumerable<ICommandMeta> _metas;
public IEnumerable<ICommandMeta> GetCommands() => _metas;
}
```
**Benefits:**
- No reflection-heavy assembly scanning
- Faster startup
- AOT-compatible
- Explicit control
### 3. Convention Over Configuration
Minimal configuration with smart defaults:
```csharp
// Default naming convention
CreateUserCommand POST /api/command/createUser
// Custom naming
[CommandName("register")]
CreateUserCommand POST /api/command/register
// Default route prefix
/api/command/* and /api/query/*
```
### 4. Type Safety
Compile-time type safety with generic constraints:
```csharp
// Type-safe registration
services.AddCommand<TCommand, TResult, THandler>()
where THandler : ICommandHandler<TCommand, TResult>;
// Compile error if types don't match
services.AddCommand<CreateUserCommand, int, WrongHandler>(); // ❌ Compile error
```
## Architectural Layers
### Typical Application Structure
```
┌─────────────────────────────────────────┐
│ Presentation Layer │
│ (HTTP Endpoints, gRPC Services) │
│ - Svrnty.CQRS.MinimalApi │
│ - Svrnty.CQRS.Grpc │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ Application Layer │
│ (Commands, Queries, Handlers) │
│ - Command/Query definitions │
│ - Handler implementations │
│ - Validators │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ Domain Layer │
│ (Business logic, Entities, Events) │
│ - Domain models │
│ - Business rules │
│ - Domain events │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ Infrastructure Layer │
│ (Data access, External services) │
│ - Repositories │
│ - Database context │
│ - External API clients │
└─────────────────────────────────────────┘
```
### Multi-Project Solution
For larger applications, use multiple projects:
```
MySolution/
├── MySolution.Api/ # HTTP/gRPC endpoints
├── MySolution.CQRS/ # Commands, queries, handlers
├── MySolution.Domain/ # Domain models, events
├── MySolution.Infrastructure/ # EF Core, repositories
└── MySolution.Tests/ # Unit and integration tests
```
## Design Patterns Used
### 1. Command Pattern
Commands encapsulate requests as objects:
```csharp
// Command (request object)
public record CreateUserCommand
{
public string Name { get; init; } = string.Empty;
}
// Handler (executes the request)
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand, int>
{
public Task<int> HandleAsync(CreateUserCommand command, CancellationToken cancellationToken)
{
// Execute logic
}
}
```
### 2. Mediator Pattern
CQRS acts as a mediator between API and business logic:
```
Client → Endpoint → Handler → Business Logic
```
No direct dependencies between client and business logic.
### 3. Strategy Pattern
Multiple implementations of same interface:
```csharp
ICommandHandler<CreateUserCommand, int>
CreateUserCommandHandler
CreateUserWithEmailCommandHandler
CreateUserWithSSOCommandHandler
```
### 4. Decorator Pattern
Validators, authorization, logging wrap handlers:
```
Client → Validation → Authorization → Handler → Business Logic
```
## Extensibility Architecture
### Extension Points
1. **Authorization**
- `ICommandAuthorizationService<TCommand>`
- `IQueryAuthorizationService<TQuery>`
2. **Query Alteration**
- `IAlterQueryableService<TSource, TDestination>`
3. **Dynamic Query Interceptors**
- `IDynamicQueryInterceptorProvider`
4. **Attributes**
- `[CommandName]`, `[QueryName]`
- `[IgnoreCommand]`, `[IgnoreQuery]`
5. **Middleware**
- ASP.NET Core pipeline integration
- Custom filters
## Performance Considerations
### Startup Performance
- **Fast startup** - Metadata pattern avoids assembly scanning
- **Minimal reflection** - Type information captured at registration
- **AOT-friendly** - No runtime type discovery
### Runtime Performance
- **Direct handler invocation** - No mediator overhead
- **DI container resolution** - Standard ASP.NET Core performance
- **Endpoint routing** - Uses built-in routing (HTTP) or gRPC runtime
### Memory Efficiency
- **Singleton metadata** - One instance per command/query type
- **Scoped handlers** - Created per request, disposed after
- **No caching layer** - Direct execution
## Security Architecture
### Defense in Depth
```
1. Network Layer (HTTPS, firewall)
2. Authentication (JWT, API keys)
3. Authorization (IAuthorizationService)
4. Validation (FluentValidation)
5. Business Rules (in handlers)
6. Data Access (parameterized queries)
```
### Built-in Security Features
- ✅ Validation before execution (FluentValidation)
- ✅ Authorization services (per command/query)
- ✅ Attribute-based endpoint control ([Ignore])
- ✅ Integration with ASP.NET Core auth
## What's Next?
Explore specific architectural topics:
- **[CQRS Pattern](cqrs-pattern.md)** - Deep dive into CQRS
- **[Metadata Discovery](metadata-discovery.md)** - How discovery works
- **[Modular Solution Structure](modular-solution-structure.md)** - Best practices for organization
- **[Dependency Injection](dependency-injection.md)** - DI patterns
- **[Extensibility Points](extensibility-points.md)** - Customization mechanisms
## See Also
- [Getting Started](../getting-started/README.md) - Build your first application
- [Best Practices](../best-practices/README.md) - Production-ready patterns
- [Tutorials: Modular Solution](../tutorials/modular-solution/README.md) - Step-by-step guide