What is CQRS?
Command Query Responsibility Segregation (CQRS) is a pattern that separates read and write operations for a data store. The write model (commands) and read model (queries) can be optimized independently.
Why CQRS?
Different Requirements
Read and write operations often have vastly different requirements:
- Reads need fast, denormalized data for display
- Writes need normalized data with business rule validation
- Read/write ratios are often 90/10 or higher
Benefits
- Independent scaling of read and write workloads
- Optimized data schemas for each operation type
- Simplified queries without complex joins
- Better security through separated concerns
Implementation
Command Side
public class CreateOrderCommand {
public Guid CustomerId { get; set; }
public List<OrderItem> Items { get; set; }
}
public class CreateOrderHandler {
public async Task Handle(CreateOrderCommand cmd) {
var order = new Order(cmd.CustomerId, cmd.Items);
await _repository.Save(order);
await _eventBus.Publish(new OrderCreated(order.Id));
}
}Query Side
public class OrderSummaryQuery {
public Guid OrderId { get; set; }
}
public class OrderSummaryHandler {
public async Task<OrderSummaryDto> Handle(OrderSummaryQuery query) {
return await _readDb.QueryFirstAsync<OrderSummaryDto>(
"SELECT * FROM OrderSummaries WHERE Id = @Id",
new { Id = query.OrderId }
);
}
}Data Synchronization
Event-Based Sync
Commands publish domain events that update the read model:
public class OrderCreatedHandler : IEventHandler<OrderCreated> {
public async Task Handle(OrderCreated evt) {
await _readDb.ExecuteAsync(@"
INSERT INTO OrderSummaries (Id, CustomerName, Total, Status)
SELECT o.Id, c.Name, o.Total, o.Status
FROM Orders o JOIN Customers c ON o.CustomerId = c.Id
WHERE o.Id = @Id",
new { Id = evt.OrderId }
);
}
}Eventual Consistency
The read model may lag behind the write model. Design your UI to handle this gracefully.
When to Use CQRS
Good Candidates
- Complex domains with many business rules
- High read/write ratio applications
- Systems requiring independent scaling
- Event-sourced systems
When to Avoid
- Simple CRUD applications
- Small teams without DDD experience
- Systems requiring strong consistency
CQRS + Event Sourcing
CQRS pairs naturally with Event Sourcing. Events become the mechanism for syncing write and read models while providing a complete audit trail.
Conclusion
CQRS adds complexity but provides powerful benefits for the right use cases. Start simple and introduce CQRS when the complexity of your domain justifies it.