CQRS pattern implementation and query optimization
Implements CQRS patterns by designing separate command and query models with optimized read stores. Triggers when building scalable systems with complex read/write requirements or performance bottlenecks.
/plugin marketplace add melodic-software/claude-code-plugins/plugin install event-modeling@melodic-softwareThis skill is limited to using the following tools:
Design and implement Command Query Responsibility Segregation patterns for scalable systems.
Before implementing CQRS:
docs-management skill for CQRS patternsTraditional 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 │
└───────────────┘ └───────────────┘
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
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
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 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 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 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");
}
}
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
// 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 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
// 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> { }
// 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;
}
}
[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);
}
}
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
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
When implementing CQRS:
For detailed guidance:
Last Updated: 2025-12-26
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.