CQRS
Separa el modelo de lectura del modelo de escritura para optimizar cada uno por separado.
Contexto
Tus lecturas y escrituras tienen formas y exigencias muy distintas: queries necesitan denormalizar y cachear; commands deben validar invariantes complejas.
Problema
Un único modelo intentando servir ambas optimiza para ninguna.
Solución
Dos pilas: Commands (mutan, devuelven solo éxito/error) y Queries (leen, devuelven DTOs adaptados a la vista).
#Ejemplo en C# — con MediatR
// Command (escritura)
public record PlaceOrderCommand(Guid CustomerId, IReadOnlyList<LineItem> Items) : IRequest<Guid>;
public class PlaceOrderHandler : IRequestHandler<PlaceOrderCommand, Guid>
{
private readonly IOrderRepository _repo;
public PlaceOrderHandler(IOrderRepository r) => _repo = r;
public async Task<Guid> Handle(PlaceOrderCommand cmd, CancellationToken ct)
{
var order = Order.Create(cmd.CustomerId, cmd.Items);
_repo.Add(order);
return order.Id;
}
}
// Query (lectura)
public record GetOrderSummaryQuery(Guid Id) : IRequest<OrderSummaryDto>;
public class GetOrderSummaryHandler : IRequestHandler<GetOrderSummaryQuery, OrderSummaryDto>
{
private readonly DbConnection _db;
public Task<OrderSummaryDto> Handle(GetOrderSummaryQuery q, CancellationToken ct) =>
_db.QueryFirstAsync<OrderSummaryDto>("SELECT id, total, status FROM order_summary WHERE id=@id", q);
}Cuándo NO aplicarlo
- Cuando el modelo es simple (CRUD): la separación añade ceremonia sin ganancia.
- Cuando el equipo no está cómodo con eventual consistency entre read/write models.
Tradeoffs
| Pro | Contra |
|---|---|
| Optimización por separado | Dos modelos a mantener |
| Reads escalan independientemente | Sincronización (eventual consistency) entre modelos |
#extra #ddd #scaling