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