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.