practice

Materialised Read Model

also called Projection, Read Model

A precomputed, query-shaped copy of data maintained asynchronously from the system of record - and the operational obligations that come with it.

cqrsprojectionsdenormalisationstalenessrebuild

A read model is a derived store shaped for how data is read rather than how it is written: a search index, a denormalised table, a cached aggregate, a graph projection. It exists because the write model is optimised for correctness and the read pattern needs something else.

Almost every non-trivial system has at least one, frequently without calling it that. Naming it matters, because a read model carries obligations that an unnamed one silently does not meet.

The four obligations

1. A stated freshness SLO. "Search reflects a listing edit within N seconds at p99." Without a number, staleness is not a property, it is a surprise, and nobody can tell whether the pipeline is working.

2. Lag monitoring and alerting. Not health of the pipeline — lag. A projection process that is running, connected and forty minutes behind passes every liveness check while serving wrong data.

3. A rebuild path that has been exercised. Projections get corrupted: a bug, a bad deploy, a partial replay. If the read model cannot be regenerated from the source of truth within an acceptable window, it has quietly become an unreliable system of record. The rebuild must be drilled, not merely designed.

4. An explicit correctness boundary. Decisions with consequences — pricing, availability at purchase, authorisation — must be validated against the source of truth, never against the projection. The read model informs; it does not decide.

Implementation patterns

  • Fed by change data capture or a domain event stream, so the write path does not synchronously depend on projection success.
  • Idempotent, upsert-based application keyed by entity id, because delivery is at-least-once.
  • Versioned projections. Build the new version alongside the old, compare, then switch reads — rather than mutating a live projection in place.
  • Per-entity replay so a single bad record can be repaired without a full rebuild.
  • Reconciliation sweeps comparing counts and checksums between source and projection, because silent drift is the characteristic failure and nothing else detects it.

Industry example

A marketplace's search index is a read model, whether or not anyone described it that way. Sellers edit listings continuously in a transactional store; an asynchronous pipeline updates a search engine that serves relevance ranking, faceting and geographic filtering that no relational schema serves well.

The instructive part is what happens when the team stops treating it as incidental. Naming it as a read model produces four immediate questions that were previously unasked: how stale can search be before the product suffers, who is paged when the pipeline lags, how long does a full reindex take, and what happens when a buyer clicks a listing that search says is available and the transactional store says is sold.

That last question is the important one, and the answer is that the checkout path re-validates against the source of truth. The read model gets the buyer to the listing; it never decides whether the sale can happen.

Failure scenarios

  • The silently wrong projection. No alert, no error, incorrect answers. Worse than an outage because nothing fires.
  • No rebuild path, discovered during the incident that requires one.
  • Business decisions made from the projection, so staleness becomes overselling or mispricing.
  • Unbounded lag with no shedding policy, where the projection falls behind and nothing degrades or warns.
  • Schema drift between source and projection after an upstream change nobody propagated.

Trade-offs

A read model buys query performance and access-pattern freedom, and it costs a pipeline, a staleness window, a rebuild capability and a second schema to evolve. When the driver is a single expensive query, a database-maintained materialised view delivers much of the benefit with none of the pipeline — the database keeps it coherent and staleness is bounded by refresh policy.

Reach for a full asynchronous projection when the read store must be a genuinely different technology, because that is when no database feature can bridge the gap.

Interview question

"Your search index is thirty minutes behind and nobody noticed until a customer complained. Walk me through what should have been in place — and tell me what you would do differently for a projection that feeds pricing rather than search."