Nivel 3 · 30 min
CQRS
CQRS (Command Query Responsibility Segregation) separa el modelo de escritura (commands) del modelo de lectura (queries). Esta separación permite optimizar cada lado de forma independiente y es el complemento natural de Event Sourcing.
Separación Command/Query
En CQRS, los Commands mutan el estado y no retornan datos (excepto confirmación). Las Queries leen el estado y no lo mutan. El modelo de escritura puede ser normalizado y enfocado en invariantes; el modelo de lectura puede ser desnormalizado y optimizado para las queries específicas de la UI.
Proyecciones y Modelos de Lectura
Las proyecciones son vistas materializadas del estado que se actualizan en respuesta a eventos. Una proyección puede ser una tabla SQL desnormalizada, un documento en MongoDB, o un índice en Elasticsearch. Diferentes vistas para diferentes casos de uso: detalle de orden, lista paginada, dashboard de analytics.
Consistencia Eventual
En CQRS con event sourcing, las proyecciones se actualizan de forma eventual —hay un lag entre el momento en que un command se procesa y el momento en que la query ve el estado actualizado. Los clientes deben diseñarse para tolerar esto (mostrar ''procesando'', usar polling o websockets). La UI puede actualizar optimistamente el estado local.
Code example
// Command Handler
@CommandHandler
public void handle(PlaceOrderCommand cmd) {
Order order = new Order(cmd.orderId(), cmd.customerId(), cmd.items());
eventStore.save(order.domainEvents());
eventBus.publish(order.domainEvents());
}
// Projection: actualiza el read model
@EventHandler
public void on(OrderPlaced event) {
orderSummaryRepo.save(new OrderSummary(
event.orderId(), event.customerId(),
event.totalAmount(), "PENDING"
));
}
// Query Handler
@QueryHandler
public List<OrderSummary> handle(GetUserOrdersQuery query) {
return orderSummaryRepo.findByCustomerId(query.customerId());
}