Command side
Validates business rules and changes state carefully.
Command Query Responsibility Segregation separates the write side from the read side when the same model becomes awkward for both. In distributed systems, this often appears when business transactions are rich and protected, while read paths need denormalized, fast, or cross-service views.
An order write model might care deeply about payment authorization and stock reservation rules, while a customer service dashboard only needs a fast denormalized view of order status, shipment state, and recent notifications. Forcing one model to do both jobs can make both sides worse.
CQRS does not mean every system needs separate databases, event sourcing, or message brokers. At its core, it means recognizing that commands and queries often have different forces. Commands protect invariants. Queries optimize retrieval and presentation. When those forces diverge, separate models can improve clarity and performance.
The pattern becomes especially useful in microservices when read models must combine information from several services or when write models are intentionally strict while read paths need flexible projections.
Validates business rules and changes state carefully.
Returns data shaped for screens, reports, or search without protecting write invariants.
Often built asynchronously from domain events or change streams.
| Situation | Why CQRS helps |
|---|---|
| Reads need denormalized cross-service data | Separate read models remove pressure from the transactional write model |
| Write rules are complex | The command model stays focused on invariants and consistency |
| Read volume is high and varied | Read projections can optimize independently for screens and reports |
| Event-driven architecture already exists | Events naturally feed query projections |
package org.javaomnibus.orders.api;
public interface PlaceOrderCommandHandler {
OrderId handle(PlaceOrderCommand command);
}
public interface OrderSummaryQuery {
OrderSummaryView findByOrderId(OrderId orderId);
}
public record OrderSummaryView(
String orderId,
String customerName,
String orderStatus,
String paymentStatus,
String shipmentStatus
) {}