Knowledge Guide
HomeSystem DesignMicroservices Patterns

Issues, Special Considerations, and Performance Implications

CQRS is not free: the moment you split one model into a write side (commands that mutate state and emit events) and a read side (denormalized views rebuilt by projectors that consume those events), you inherit an asynchronous gap between "the write committed" and "every read reflects it" — and because the two sides live in different stores, you also lose the single ACID transaction that CRUD gave you for free. Everything below is a consequence of those two facts.

The mechanism the analogies skipped: how the read model actually gets updated

In a real CQRS deployment the read model is not updated by the same code path that handles the command. The command handler writes to the authoritative write store and emits a domain event (e.g. OrderPlaced); that event is published to a log or broker (Kafka, a transactional outbox, an event store); a separate projector consumes the event and upserts into one or more read stores (a denormalized Postgres view, an Elasticsearch index, a Redis cache). The lag between commit and projection is the "stale read" window — it is a physical property of the pipeline, not a bug. Typical numbers on a healthy Kafka pipeline: single-digit to low-tens of milliseconds; under consumer lag or a rebuild, seconds to minutes.

diagram
diagram

Worked trace: a customer places an order, then immediately refreshes

E-commerce checkout. Write model owns orders (normalized); read model serves GET /orders from a denormalized order_summary table updated by a Kafka consumer. The client fires the query 20ms after the command returns 202 Accepted.

t (ms)ActorActionRead store state for order #4711
0Command handlerPlaceOrder commits row to write storeabsent
+2OutboxRow written to outbox in the same DB transactionabsent
+5RelayPublishes OrderPlaced{id:4711} to Kafkaabsent
+20ClientGET /orders reads read storeabsent → UI shows "no orders"
+40ProjectorConsumes event (consumer poll interval)absent
+45ProjectorUpserts order_summary rowpresent
+60Client (retry)GET /orders againpresent → correct

The user hit a 45ms hole and saw an empty list on a purchase they just made. Nothing failed; the pipeline is working exactly as designed. Mitigations, in order of cost: (1) after a command, have the UI optimistically render the known result instead of re-querying; (2) return the new entity's version/offset and let the read side read-your-writes by waiting for the projector to reach that offset; (3) route the immediately-following read to the write store; (4) accept it and set user expectations. Do not "fix" it by writing to the read store synchronously inside the command handler — that recouples the two sides and reintroduces the dual-write problem below.

The transaction problem, mechanically — and its real fix

The original page raised "what if one command in a multi-step transaction fails?" but stopped at the wall-painting analogy. The mechanism: across services (Order, Payment, Inventory) there is no distributed ACID transaction — you cannot ROLLBACK a payment that already captured money in another service's database. The standard answer is a saga: model the flow as a sequence of local transactions, each emitting an event, and for every step define a compensating transaction that semantically undoes it.

  1. OrderCreated (Order svc, local commit)
  2. PaymentCaptured (Payment svc, local commit)
  3. ReserveInventory fails — out of stock
  4. Saga fires compensations in reverse: RefundPayment, then CancelOrder. Note the order is not deleted — it moves to a CANCELLED state. Compensation is forward motion, not an erase.

This is why CQRS pushes you toward event sourcing and sagas: once the read model is event-driven, modeling the write side as events makes compensation and audit natural. The cost is that every operation now needs a designed inverse, and intermediate states (payment captured, order not yet confirmed) are briefly visible.

Pitfalls

When to use CQRS — and when not to

Reach for it when concrete signals line up: a highly skewed read:write ratio (say 100:1) where you want to scale and cache reads independently; queries that need a shape the write schema can't serve cheaply (denormalized dashboards, full-text search, graph-like joins); a collaborative/high-contention domain where separating writes reduces lock pressure; or a domain that already benefits from event sourcing and audit.

Prefer simpler tools when those signals are absent:

Rule of thumb: add CQRS to the specific bounded context that has the skew or the shape mismatch — never blanket the whole system. Most services should stay CRUD.

Takeaways


Sources: Chris Richardson, Microservices Patterns (Manning) — CQRS, Saga, and Transactional Outbox chapters; Martin Fowler, "CQRS" and "Event Sourcing" (martinfowler.com); Greg Young's original CQRS/event-sourcing talks; Microsoft Azure Architecture Center CQRS & Materialized View pattern guides; Confluent documentation on Kafka consumer lag and exactly-once/idempotent processing. Re-authored/Deepened for this guide.

🤖 Don't fully get this? Learn it with Claude

Stuck on Issues, Special Considerations, and Performance Implications? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Issues, Special Considerations, and Performance Implications** (System Design) and want to truly understand it. Explain Issues, Special Considerations, and Performance Implications from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Issues, Special Considerations, and Performance Implications** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Issues, Special Considerations, and Performance Implications** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Issues, Special Considerations, and Performance Implications** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes