Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Read Models
HLD

Read Models

Projections shaped for queries — building and maintaining the denormalized views CQRS runs on.

A Read Model Is

 a DENORMALIZED view pre-shaped for specific queries:

 write model (normalized):
   users(id, name)  orders(id, user_id, total) ...

 read model "user_order_history":
   {
     user_id, user_name, 
     orders: [{id, total, status, placed_at}, ...],
     lifetime_value: 4310.50,
     last_order_at: ...
   }
 one query serves the profile page — zero joins at read time.

 joins happen ONCE during projection updates,
 not on every page load. that's the whole trick.

Building Projections

 from events (or CDC):

 handler(OrderPlaced e):
   upsert user_order_history[e.user_id]:
     push order {e.id, e.total, ...}
     lifetime_value += e.total
     last_order_at = e.at

 properties to design deliberately:
 □ IDEMPOTENT handlers (redelivery!): upserts by event id,
   or track last-processed offset per projection
 □ ORDERED per key: sequence/version guards against
   stale-event overwrites
 □ REBUILDABLE: drop + replay from source = migration tool,
   bug-recovery path, new-view creation mechanism

 the rebuild property is what makes aggressive iteration safe:
 views are DISPOSABLE; the stream is truth.

One Query Shape → One View

 anti-pattern: god-view serving every endpoint.
 pattern: purpose-built views:

 view                | store        | shape
 --------------------|--------------|------------------
 search              | elasticsearch| inverted index
 profile page        | postgres json| document blob
 counters/badges     | redis hashes | key-counters
 recommendations     | feature store| vectors/lists

 each view: its own lag tolerance, its own scaling curve,
 its own rebuild pipeline. independence is the feature.

 cost honesty: N views = N pipelines to monitor/operate.
 consolidate where shapes genuinely overlap.

Serving With Staleness

 read models lag writes by sync latency:

 UX patterns for eventual reads:
 - OPTIMISTIC UI: show expected state immediately;
   reconcile when view catches up ("Order confirmed ✓")
 - WRITE-THROUGH HYBRID: response of the command RETURNS
   the new state (client renders it; view catches up behind)
 - READ-YOUR-WRITES routing: this-user queries hit write
   store briefly after their mutation; everyone else sees views
 - STALENESS INDICATORS: "updated 30s ago" where precision matters

 pick per VIEW: cart needs read-your-writes; 
 recommendation feeds don't care.

Interview Framing

“Profile pages take 800ms with 12 joins — fix it” scored answer: recognize read-shape mismatch, propose purpose-built read model updated via CDC (not request-time caching!), design the projection handler idempotently, staleness policy stated (read-your-writes post-mutation), rebuildability mentioned as safety net. The “build the read model, not a cache” distinction is precisely what’s being tested.

My Private Notes

Notes are auto-saved locally to this device.