322 lines
9.3 KiB
Markdown
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
|