# Introduction to CQRS Learn what CQRS is, when to use it, and how Svrnty.CQRS implements the pattern. ## What is CQRS? **CQRS** stands for **Command Query Responsibility Segregation**. It's an architectural pattern that separates read operations (queries) from write operations (commands). ### Traditional Approach In traditional architectures, the same model handles both reads and writes: ```csharp // Traditional approach - same service for everything public class UserService { public void CreateUser(CreateUserDto dto) { /* write */ } public void UpdateUser(UpdateUserDto dto) { /* write */ } public UserDto GetUser(int id) { /* read */ } public List SearchUsers(string criteria) { /* read */ } } ``` ### CQRS Approach CQRS separates these responsibilities: ```csharp // Commands (write operations) public class CreateUserCommandHandler : ICommandHandler { public Task HandleAsync(CreateUserCommand command, CancellationToken cancellationToken) { // Write logic only } } // Queries (read operations) public class GetUserQueryHandler : IQueryHandler { public Task HandleAsync(GetUserQuery query, CancellationToken cancellationToken) { // Read logic only } } ``` ## Core Concepts ### Commands Commands represent **write operations** that change system state. **Characteristics:** - ✅ Imperative names (CreateUser, UpdateOrder, DeleteProduct) - ✅ Contain all data needed for the operation - ✅ May or may not return a result - ✅ Can be validated before execution - ✅ Typically have side effects **Example:** ```csharp public record PlaceOrderCommand { public int CustomerId { get; init; } public List Items { get; init; } = new(); public decimal TotalAmount { get; init; } } ``` ### Queries Queries represent **read operations** that return data without changing state. **Characteristics:** - ✅ Question-based names (GetUser, SearchOrders, FetchProducts) - ✅ Never modify state - ✅ Always return data - ✅ Can be cached - ✅ Should be idempotent **Example:** ```csharp public record GetOrderQuery { public int OrderId { get; init; } } ``` ### Handlers Handlers contain the actual business logic for commands and queries. **Command Handler:** ```csharp public class PlaceOrderCommandHandler : ICommandHandler { public async Task HandleAsync(PlaceOrderCommand command, CancellationToken cancellationToken) { // Validate business rules // Save to database // Emit events // Return order ID return orderId; } } ``` **Query Handler:** ```csharp public class GetOrderQueryHandler : IQueryHandler { public async Task HandleAsync(GetOrderQuery query, CancellationToken cancellationToken) { // Fetch from database // Map to DTO // Return data return orderDto; } } ``` ## Why Use CQRS? ### Benefits 1. **Separation of Concerns** - Commands focus on business logic and validation - Queries focus on data retrieval and formatting - Easier to understand and maintain 2. **Scalability** - Scale reads and writes independently - Optimize databases differently (write DB vs read DB) - Use read replicas for queries 3. **Flexibility** - Different models for reading and writing - Optimize queries without affecting commands - Easy to add new queries without changing commands 4. **Security** - Fine-grained authorization (per command/query) - Easier to audit write operations - Clear boundaries for access control 5. **Testing** - Handlers are easy to unit test - Clear inputs and outputs - Mock dependencies easily 6. **Maintainability** - Small, focused handlers - Single Responsibility Principle - Easy to add new features ### Trade-offs 1. **Increased Complexity** - More files and classes - Learning curve for team - Might be overkill for simple CRUD 2. **Consistency Challenges** - With separate read/write models, eventual consistency may be required - Requires careful design 3. **Code Duplication** - Some logic might be repeated - More boilerplate code ## When to Use CQRS ### ✅ Good Fit - **Complex business logic** - Commands with validation, rules, and workflows - **Different read/write patterns** - Complex queries vs simple writes - **High scalability needs** - Read-heavy or write-heavy systems - **Audit requirements** - Need to track all changes - **Event sourcing** - Natural fit with event-driven architectures - **Microservices** - Clear boundaries between services ### ❌ Not Recommended - **Simple CRUD** - Basic create/read/update/delete operations - **Small applications** - Overhead not justified - **Tight deadlines** - Team not familiar with pattern - **Consistent data models** - Same model for reads and writes ## How Svrnty.CQRS Works Svrnty.CQRS provides a lightweight, production-ready implementation: ### 1. Define Commands and Queries ```csharp // Just POCOs (Plain Old CLR Objects) public record CreateProductCommand { public string Name { get; init; } = string.Empty; public decimal Price { get; init; } } ``` ### 2. Implement Handlers ```csharp public class CreateProductCommandHandler : ICommandHandler { private readonly IProductRepository _repository; public CreateProductCommandHandler(IProductRepository repository) { _repository = repository; } public async Task HandleAsync(CreateProductCommand command, CancellationToken cancellationToken) { var product = new Product { Name = command.Name, Price = command.Price }; await _repository.AddAsync(product, cancellationToken); return product.Id; } } ``` ### 3. Register in DI ```csharp builder.Services.AddCommand(); ``` ### 4. Automatic Endpoint Generation Svrnty.CQRS automatically creates HTTP or gRPC endpoints: **HTTP:** ``` POST /api/command/createProduct ``` **gRPC:** ```protobuf rpc CreateProduct (CreateProductRequest) returns (CreateProductResponse); ``` ### 5. Built-in Features - ✅ **Validation** - FluentValidation integration - ✅ **Discovery** - Metadata-driven endpoint generation - ✅ **Authorization** - Custom authorization services - ✅ **Protocols** - HTTP (Minimal API) and gRPC support - ✅ **Dynamic Queries** - OData-like filtering - ✅ **Event Streaming** - Event sourcing and projections ## Architecture Overview ``` ┌─────────────────┐ │ HTTP/gRPC │ ← Automatic endpoint generation │ Endpoints │ └────────┬────────┘ │ ┌────────▼────────┐ │ Validation │ ← FluentValidation │ (Optional) │ └────────┬────────┘ │ ┌────────▼────────┐ │ Handler │ ← Your business logic │ (Command/Query)│ └────────┬────────┘ │ ┌────────▼────────┐ │ Data Layer │ ← Database, external APIs, etc. │ (Your choice) │ └─────────────────┘ ``` ## Key Principles in Svrnty.CQRS 1. **Convention over Configuration** - Minimal setup required - Automatic endpoint naming - Sensible defaults 2. **Metadata-Driven Discovery** - Handlers registered as metadata - Runtime enumeration for endpoint generation - Type-safe at compile time 3. **Framework Agnostic** - Works with any data access layer (EF Core, Dapper, etc.) - No prescribed database or ORM - Integration points are interfaces 4. **Production Ready** - Validation, authorization, observability - Health checks, metrics, structured logging - Event sourcing and consumer groups ## What's Next? Now that you understand CQRS, let's get your development environment set up! **Continue to [Installation](02-installation.md) →** ## See Also - [Architecture: CQRS Pattern](../architecture/cqrs-pattern.md) - Deeper dive into the pattern - [Architecture: Metadata Discovery](../architecture/metadata-discovery.md) - How discovery works - [Best Practices: Command Design](../best-practices/command-design.md) - Designing effective commands - [Best Practices: Query Design](../best-practices/query-design.md) - Query optimization patterns