Recipe: CQRS design walkthrough
Separate reads from writes with a single-source-of-truth event log.
1. Command side
Accept a command payload, validate it, and append an immutable event to the write-optimized store. Return a 202 with a correlation id — never the final state.
POST /api/orders/commands
{
"type": "PlaceOrder",
"payload": { "sku": "NMB-01", "qty": 1 }
}
→ 202 { "correlationId": "evt_9a7c" }2. Projection pipeline
A background worker tails the event log and projects each event into a read-optimized materialized view. Idempotency is enforced via event id dedup.
Worker loop:
for event in stream:
if already_seen(event.id): skip
handler = registry[event.type]
handler.apply(read_db, event)
mark_seen(event.id)3. Query side
Reads hit the projected view directly — no joins, no aggregates at query time. The API returns denormalized JSON shaped exactly for the UI.
GET /api/orders/queries/active?userId=u_42
→ 200 { "orders": [ { "id": "o_1", "status": "shipped" } ] }4. Consistency model
The system is eventually consistent. The projection lag is typically under 200ms. Clients poll with the correlation id or subscribe to a WebSocket for real-time confirmation.