Claude-skill-registry cqrs-architecture
CQRS pattern implementation and query optimization
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/cqrs-architecture" ~/.claude/skills/majiayu000-claude-skill-registry-cqrs-architecture && rm -rf "$T"
manifest:
skills/data/cqrs-architecture/SKILL.mdsource content
CQRS Architecture Skill
Design and implement Command Query Responsibility Segregation patterns for scalable systems.
MANDATORY: Documentation-First Approach
Before implementing CQRS:
- Invoke
skill for CQRS patternsdocs-management - Verify patterns via MCP servers (perplexity, context7)
- Base guidance on established CQRS literature
CQRS Fundamentals
Traditional vs CQRS: TRADITIONAL (Single Model): ┌─────────────────────────────────┐ │ Application │ ├─────────────────────────────────┤ │ Domain Model │ │ (Reads + Writes) │ ├─────────────────────────────────┤ │ Database │ └─────────────────────────────────┘ CQRS (Separated Models): ┌───────────────┐ ┌───────────────┐ │ Command Side │ │ Query Side │ │ (Write Model) │ │ (Read Model) │ ├───────────────┤ ├───────────────┤ │ Domain Logic │ │ DTO/Views │ │ Aggregates │ │ Projections │ ├───────────────┤ ├───────────────┤ │ Write DB │───►│ Read DB │ └───────────────┘ └───────────────┘
CQRS Levels
Level 1: Logical Separation
Same database, separate code paths: ┌─────────────────────────────────────┐ │ Application │ ├──────────────────┬──────────────────┤ │ Command Handlers │ Query Handlers │ │ - Validation │ - Direct SQL │ │ - Domain Logic │ - Projections │ │ - Events │ - DTOs │ ├──────────────────┴──────────────────┤ │ Single Database │ └─────────────────────────────────────┘ Benefits: ✓ Clean separation in code ✓ Simple deployment ✓ Single source of truth ✓ Good starting point
Level 2: Separate Read Models
Same write DB, separate read DB: ┌─────────────────┐ ┌─────────────────┐ │ Command Side │ │ Query Side │ ├─────────────────┤ ├─────────────────┤ │ Command Handler │ │ Query Handler │ │ Domain Model │ │ DTOs │ ├─────────────────┤ ├─────────────────┤ │ Write Database │───►│ Read Database │ │ (Normalized) │sync│ (Denormalized) │ └─────────────────┘ └─────────────────┘ Benefits: ✓ Optimized read performance ✓ Scale reads independently ✓ Different storage technologies ✓ Eventually consistent reads
Level 3: Event-Sourced CQRS
Event store as write model, projections as read: ┌─────────────────┐ ┌─────────────────┐ │ Command Side │ │ Query Side │ ├─────────────────┤ ├─────────────────┤ │ Command Handler │ │ Query Handler │ │ Aggregate │ │ Read Models │ ├─────────────────┤ ├─────────────────┤ │ Event Store │───►│ Multiple Read │ │ (Append-only) │ │ Databases │ └─────────────────┘ └─────────────────┘ Benefits: ✓ Complete audit trail ✓ Temporal queries ✓ Multiple projections ✓ Rebuild read models
Command Side Design
Command Structure
// Command Definition public record PlaceOrderCommand( Guid CustomerId, List<OrderItemDto> Items, string ShippingAddress ) : ICommand<OrderId>; // Command Handler public class PlaceOrderHandler : ICommandHandler<PlaceOrderCommand, OrderId> { private readonly IOrderRepository _repository; private readonly IEventPublisher _events; public async Task<OrderId> HandleAsync( PlaceOrderCommand command, CancellationToken ct) { // Validation if (!command.Items.Any()) throw new ValidationException("Order must have items"); // Domain logic var order = Order.Create( command.CustomerId, command.Items.Select(i => new OrderItem(i.ProductId, i.Quantity))); // Persistence await _repository.SaveAsync(order, ct); // Publish events await _events.PublishAsync(order.GetDomainEvents(), ct); return order.Id; } }
Command Patterns
Command Best Practices: NAMING: - Imperative: PlaceOrder, CancelOrder, UpdateAddress - Include context: not just "Create" but "CreateOrder" STRUCTURE: - Immutable (records) - Only data needed for operation - No business logic in command VALIDATION: - Input validation in handler - Business validation in domain - Return meaningful errors IDEMPOTENCY: - Include idempotency key - Handle duplicate submissions - Return same result for retries
Query Side Design
Query Structure
// Query Definition public record GetOrderByIdQuery(Guid OrderId) : IQuery<OrderDetailsDto>; // Query Handler public class GetOrderByIdHandler : IQueryHandler<GetOrderByIdQuery, OrderDetailsDto> { private readonly IReadDbContext _db; public async Task<OrderDetailsDto> HandleAsync( GetOrderByIdQuery query, CancellationToken ct) { var order = await _db.OrderDetails .Where(o => o.OrderId == query.OrderId) .Select(o => new OrderDetailsDto { OrderId = o.OrderId, CustomerName = o.Customer.Name, Items = o.Items.Select(i => new OrderItemDto { ProductName = i.ProductName, Quantity = i.Quantity, Price = i.Price }).ToList(), Status = o.Status, TotalAmount = o.TotalAmount }) .FirstOrDefaultAsync(ct); return order ?? throw new NotFoundException("Order not found"); } }
Read Model Optimization
Query Optimization Strategies: 1. DENORMALIZATION - Pre-join data - Store calculated values - Flatten hierarchies 2. MATERIALIZED VIEWS - Database-managed - Automatically updated - Query-optimized 3. CACHING - In-memory for hot data - Distributed for shared - Invalidate on events 4. SPECIALIZED STORES - ElasticSearch for search - Redis for real-time - ClickHouse for analytics
Synchronization Patterns
Projection from Events
// Event-Driven Projection public class OrderProjection : IEventHandler<OrderPlaced>, IEventHandler<OrderShipped> { private readonly IOrderViewRepository _views; public async Task HandleAsync(OrderPlaced @event, CancellationToken ct) { var view = new OrderView { OrderId = @event.OrderId, CustomerId = @event.CustomerId, Status = "Placed", PlacedAt = @event.Timestamp, ItemCount = @event.Items.Count, TotalAmount = @event.TotalAmount }; await _views.InsertAsync(view, ct); } public async Task HandleAsync(OrderShipped @event, CancellationToken ct) { await _views.UpdateAsync(@event.OrderId, view => { view.Status = "Shipped"; view.ShippedAt = @event.Timestamp; view.TrackingNumber = @event.TrackingNumber; }, ct); } }
Consistency Patterns
Consistency Options: STRONG CONSISTENCY (Same Transaction): ┌──────────┐ ┌──────────┐ │ Command │───►│ Read │ │ DB │ │ Model │ │ │ │ Update │ └──────────┴────┴──────────┘ Same Transaction EVENTUAL CONSISTENCY (Async): ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Command │───►│ Message │───►│ Read │ │ DB │ │ Queue │ │ Model │ └──────────┘ └──────────┘ └──────────┘ Async, Eventually Consistent HYBRID (Read-Your-Writes): - Immediate read from command side - Eventually consistent for others - Version checking in queries
MediatR Implementation
Setup with MediatR
// Registration services.AddMediatR(cfg => { cfg.RegisterServicesFromAssembly(typeof(Program).Assembly); }); // Command/Query Interfaces public interface ICommand<TResult> : IRequest<TResult> { } public interface IQuery<TResult> : IRequest<TResult> { } // Handler Interfaces public interface ICommandHandler<TCommand, TResult> : IRequestHandler<TCommand, TResult> where TCommand : ICommand<TResult> { } public interface IQueryHandler<TQuery, TResult> : IRequestHandler<TQuery, TResult> where TQuery : IQuery<TResult> { }
Pipeline Behaviors
// Validation Behavior public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull { private readonly IEnumerable<IValidator<TRequest>> _validators; public async Task<TResponse> Handle( TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct) { var failures = _validators .Select(v => v.Validate(request)) .SelectMany(r => r.Errors) .Where(f => f != null) .ToList(); if (failures.Any()) throw new ValidationException(failures); return await next(); } } // Logging Behavior public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull { private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger; public async Task<TResponse> Handle( TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct) { _logger.LogInformation("Handling {RequestType}", typeof(TRequest).Name); var response = await next(); _logger.LogInformation("Handled {RequestType}", typeof(TRequest).Name); return response; } }
API Design with CQRS
REST API Pattern
[ApiController] [Route("api/orders")] public class OrdersController : ControllerBase { private readonly IMediator _mediator; // Commands use POST/PUT/DELETE [HttpPost] public async Task<ActionResult<OrderId>> PlaceOrder( [FromBody] PlaceOrderCommand command, CancellationToken ct) { var orderId = await _mediator.Send(command, ct); return CreatedAtAction(nameof(GetOrder), new { id = orderId }, orderId); } // Queries use GET [HttpGet("{id}")] public async Task<ActionResult<OrderDetailsDto>> GetOrder( Guid id, CancellationToken ct) { var order = await _mediator.Send(new GetOrderByIdQuery(id), ct); return Ok(order); } [HttpGet] public async Task<ActionResult<PagedResult<OrderSummaryDto>>> ListOrders( [FromQuery] ListOrdersQuery query, CancellationToken ct) { var orders = await _mediator.Send(query, ct); return Ok(orders); } }
When to Use CQRS
Good Fit
CQRS Works Well For: ✓ Complex reads AND writes - Different optimization needs - Read/write ratio imbalance ✓ Multiple views of data - Different query patterns - Multiple UI requirements ✓ Collaborative domains - Many concurrent users - Complex validation ✓ Event-driven systems - Microservices - Async processing ✓ Scalability requirements - Independent read/write scaling - Performance optimization
Poor Fit
CQRS May Not Fit: ✗ Simple CRUD applications - Overhead not justified - Same model works fine ✗ Small team/project - Added complexity - Maintenance burden ✗ Strong consistency required - Real-time requirements - Financial transactions ✗ Unknown query patterns - Ad-hoc reporting - BI requirements
Workflow
When implementing CQRS:
- Evaluate Fit: Is CQRS appropriate for this context?
- Choose Level: Logical, physical, or event-sourced?
- Design Commands: Identify write operations
- Design Queries: Identify read patterns
- Plan Sync: How will read models be updated?
- Implement Pipeline: Validation, logging, etc.
- Consider Consistency: What guarantees are needed?
- Test Both Sides: Command and query testing
References
For detailed guidance:
Last Updated: 2025-12-26