System Design Examples
CQRS in system design — use cases with teeth
CQRS is overkill for many apps and indispensable for a few. Design reviews should demand a bottleneck, a rejected alternative, and a staleness budget — not a slogan about "separating reads and writes."
Where CQRS shines
- Read-heavy product surfaces — browse/search QPS orders of magnitude above admin/catalog writes.
- Complex write domain, simple queries — rich invariants on write; flat DTOs on read.
- Collaborative domains — commands can be rejected with rules; queries see materialized state (still need concurrency control on write).
- Real-time analytics adjacent to OLTP — do not run heavy aggregations on the primary write DB.
Worked example: e-commerce order + customer timeline
Write model: Order service — place, cancel, pay; normalized tables; strong invariants (cannot ship unpaid).
Read models: (1) Customer "my orders" list denormalized for mobile; (2) Support console search by email/phone in Elasticsearch; (3) Warehouse pick-list projection.
Flow: PlaceOrder command → write DB + outbox OrderPlaced → projectors update three stores. Mobile list lag SLO: p99 < 2 s. Support search lag: < 30 s acceptable.
Numbers: 300 place-order/s writes; 15,000 "my orders" reads/s. Read model in Redis/itemized store absorbs reads; write DB sized for 300 TPS + headroom, not 15k QPS joins.
Event-driven microservices + CQRS
Order lifecycle stages (placed, paid, shipped) as events feed shipping and notification read needs without coupling to Order's internal tables. Shipping service owns its write model for shipment entities; it does not UPDATE Order's DB (no shared database anti-pattern).
Why the outbox: the dual-write crash window
The naive way to feed projectors is a dual write: commit the state change to the write DB, then publish the event to the broker. These are two separate systems with no shared transaction, so a crash in the gap between them silently loses the event:
1. BEGIN; UPDATE orders SET status='PLACED' WHERE id=O-8842; COMMIT; // state durable
2. --- process crashes here (or the broker is briefly unreachable) ---
3. publish OrderPlaced(O-8842) to broker // NEVER happens
4. projectors never see O-8842 → "my orders", search, pick-list all miss it — permanently
The order is real in the write DB but invisible in every read model, and nothing ever retries the publish — the divergence is silent and permanent. The transactional outbox closes the window: write the event into an outbox row in the same local transaction as the state change, so both commit or neither does.
BEGIN;
UPDATE orders SET status='PLACED' WHERE id=O-8842;
INSERT INTO outbox(id, type, payload) VALUES(evt-91, 'OrderPlaced', {...});
COMMIT; // atomic: state + event land together
// a separate relay (poller or CDC tailing the DB log) reads outbox rows and publishes them
The relay delivers at-least-once (it may re-publish after its own crash), so projectors must be idempotent: dedupe on the event id, or carry a monotonic version per aggregate and ignore any event whose version is ≤ the one already applied. That is the full answer to "why the outbox" — it converts a lossy dual-write into an atomic write plus an idempotent, retryable delivery.
CQRS ≠ event sourcing
These are separate decisions often bundled together. CQRS only splits the write model from one or more read models — the write DB still stores current state (a row you UPDATE in place). Event sourcing (ES) goes further: the append-only event log becomes the source of truth, and current state is a fold over past events. ES buys you replay (rebuild any projection, or a brand-new one, from history) and temporal queries ("what did this order look like last Tuesday?"). It costs you snapshotting (folding millions of events per read is infeasible, so you persist periodic snapshots), event schema evolution (old events must stay readable forever), and eventual consistency everywhere. Do CQRS without ES when you want read-scaling and purpose-built query models but current-state storage is fine; reach for ES only when replay or an audit-grade history genuinely earns those costs.
Hostile design-review table
| System | First bottleneck under load | Rejected alternative | Trade-off paid |
|---|---|---|---|
| Catalog browse + admin edits | Primary CPU on browse joins; or projector lag on big reindex | Scale write DB vertically forever for browse | Stale prices/cards; dual deploy of projectors |
| Social feed reads vs post writes | Fan-out-on-write projection cost for celebrities | Fully sync fan-out in request path | Hybrid push/pull; lag for some followers |
| Simple CRUD todo app | — | CQRS | Reject — one model wins |
| Inventory during flash sale | Oversell if stock only on lagging read model | Check stock only in Elasticsearch | Reservation on write path; read model is display-only |
When NOT to apply CQRS in the design
- Read/write load and shapes are similar.
- Team size small; operational budget thin.
- Hard requirement: every read sees latest write with no extra tokens/sessions — prefer single model or carefully scoped read-your-writes.
Operability
- Lag SLIs per projection.
- Rebuild pipeline tested (from snapshot + events or from source).
- Version fields to detect out-of-order applies.
- Alert on projector error rate and DLQ.
Drill ladder
- Q: Can inventory truth live only in the read model? A: No for correctness-critical stock; display can be projected, reservations/commits on write model.
- Q: Why outbox between write model and projectors? A: Avoid dual-write loss so projections do not miss commits.
- Q: Interviewer: "Isn't this just cache?" A: Cache is usually same model, optional, TTL/invalidation; CQRS read model is a first-class, purpose-built query model updated by explicit projection logic.
🤖 Don't fully get this? Learn it with Claude
Stuck on System Design Examples? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **System Design Examples** (System Design) and want to truly understand it. Explain System Design Examples 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.
Socratic — adapts to where you're stuck.
Teach me **System Design Examples** 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.
Active recall exposes what you missed.
Quiz me on **System Design Examples** 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.
Intuition + hook + flashcards for long-term memory.
Help me remember **System Design Examples** 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.