A team builds three different read models (a SQL table for a dashboard, an Elasticsearch index for search, a Redis hash for a single-item lookup) all fed from the same stream of domain events. What problem does this design solve, and what new problems does it introduce?
answer
- polyglot persistence: right store per access pattern
- each projector consumes independently -> independent lag
- read models can disagree with each other, not just the write side
- monitor per-projector lag + reconciliation
- rebuild cost multiplies by number of read models
basics
~20 sBuilding several different read models (like a search index, a dashboard table, and a fast key-value lookup) all from the same underlying events lets each one be fast for its own job. The cost is you now have several copies of the truth to keep in sync and debug when they disagree.
solid answer
~50 sMultiple purpose-built projections from the same event stream let each read model be optimized for its access pattern — a search index for full-text queries, a relational table for structured dashboard queries, a key-value store for single-key lookups by ID — instead of forcing one generic store to serve all of them poorly. This solves the 'one schema can't be good at everything' problem. It introduces new problems: each projector is an independent consumer that can lag or fail at a different rate, so the read models can disagree with each other, not just with the write model, at any given moment; operational surface area grows (N stores to run, monitor, and version); and consumers who join data across two read models (e.g., search results cross-referenced against the dashboard table) can get inconsistent combined results because the two projections aren't guaranteed to be at the same point in the event stream.
go deeper
Should recognize that different kinds of screens might need different kinds of storage, in plain terms — 'search needs a search tool, a fast lookup needs something quick.'
Should explain that each read model is built by its own subscriber and name at least one plausible reason they might get out of sync with each other (different processing speed).
Should discuss concretely how to detect and handle divergence between read models — lag monitoring, reconciliation, and how to decide what a user sees when two projections disagree.
Should weigh polyglot persistence against operational complexity as an architectural decision, discuss rebuild/versioning strategy across N stores, and give a real scenario where cross-read-model inconsistency caused a customer-visible problem.
## How the mechanism works The mechanism here is straightforward in principle: the write side emits a single, ordered stream of domain events (e.g. `ProductCreated`, `PriceChanged`, `ProductDiscontinued`) to something like Kafka, an event store, or a transactional outbox, and each read model is maintained by its own independent **projector** — a consumer that subscribes to that stream, applies the events relevant to it, and writes the result into whatever storage technology suits its query pattern. | The projector | What it builds | |---|---| | An Elasticsearch-backed projector | An inverted index for full-text search and faceted filtering. | | A relational projector | Normalized-enough materialized views and pre-joined tables for a reporting dashboard that needs SQL aggregation. | | A Redis-backed projector | Flat hashes keyed by product ID for sub-millisecond single-item lookups on a product detail page. | All three consume the same events, but independently, at their own pace, using their own schema. ## What the design buys you This is a deliberate application of **polyglot persistence**: no single storage technology is good at everything, so rather than compromising and picking one database that's 'okay' at search, 'okay' at reporting, and 'okay' at key lookups, the team gets a store that's genuinely good at each job. It also isolates failure and scaling: - if the search cluster falls over, the product-detail page (served from Redis) keeps working - if the dashboard's reporting queries are heavy and slow, they don't compete for the same database connections or cache memory as the customer-facing lookup path Each projection can also be scaled, tuned, and even redesigned independently — reindexing search doesn't require touching the Redis schema. ## What it costs The trade-offs are real and compound with each additional read model. - **Operationally**, the team now runs, monitors, backs up, and versions N different storage technologies instead of one, each with its own failure modes, upgrade cadence, and on-call runbook. - **More subtly**, the projectors are independent consumers of the same stream, but nothing guarantees they process events at the same rate — Elasticsearch indexing is typically slower than a Redis `SET`, so at any given moment the search index can be meaningfully further behind the write model than the Redis cache is. - That means the read models don't just individually lag the write model (ordinary eventual consistency); they can disagree with each other. A consumer that reads from two read models in the same request — say, showing search results (from Elasticsearch) annotated with 'in stock' badges pulled live from Redis — can present an internally inconsistent view: a product that Elasticsearch still shows as available because its projector hasn't caught up to a `DiscontinueProduct` event, badge-annotated as 'in stock' because Redis has already processed it, or vice versa. ## Failure modes Failure modes concentrate around lag divergence and rebuild cost. 1. **Lag divergence.** If one projector has a bug — say, a schema change on the write side adds a new event type the Elasticsearch projector doesn't know how to handle and it starts throwing and retrying — that one read model quietly falls further and further behind while the others keep pace, and unless there's per-projector lag monitoring, nobody notices until customers report that search results are stale while everything else looks fine. 2. **Rebuild cost.** Rebuilds get more expensive too: what used to be 'replay the event log to repopulate one table' becomes 'replay the same event log N times, once per read model, potentially at different speeds, and now the team has to reason about which combination of read models is consistent with which combination of others during the rebuild window.' 3. **Idempotency.** Idempotency also has to be solved per projector — consumer group offsets, retries, and rebalances mean each projector needs its own de-duplication or idempotent-apply logic; a bug in one doesn't affect the others, but it does mean the guarantees aren't uniform across read models unless the team enforces the same discipline everywhere. ## Where it bites in practice A concrete version of this scenario: an e-commerce catalog publishes `ProductCreated`, `PriceChanged`, and `ProductDiscontinued` events to a message stream. Three projectors consume it — one builds an Elasticsearch index for the search page, one builds a Redis hash for the product-detail page's fast lookup, one builds a Postgres materialized view for the merchandising team's pricing dashboard. During a flash sale, a burst of `PriceChanged` events arrives; Redis, being simple key overwrites, catches up in milliseconds, while Elasticsearch's bulk-indexing pipeline lags by tens of seconds under the load. For those tens of seconds, a shopper can see the new sale price on the product page (from Redis) but find the product missing from a price-filtered search ('under $20') because the search index still reflects the old price (from the lagging Elasticsearch projector). Neither read model is 'wrong' — both are legitimate, eventually-consistent projections of the same event stream — but the two together tell an inconsistent story, and the team has to decide, consciously, how much of that inconsistency the product is willing to expose to users versus mask with UX (e.g., 'prices may take a moment to update in search').
- How would you design monitoring to catch cross-read-model inconsistency before customers notice, in a system with multiple projections from one event stream?Track per-projector consumer lag (how far behind the head of the stream each one is) as a first-class metric with alerting thresholds, and run periodic reconciliation checks that compare a sample of records across read models — or against the write side — for disagreement. Lag alone tells you a model might be stale; cross-model reconciliation tells you it actually produced a visibly wrong combined answer.
- If two read models disagree because one lags the other, should the system show the more 'confident'/fresher one, block until they agree, or expose the discrepancy to the user?It depends on the business cost of inconsistency versus latency: for low-stakes UX (search facets, view counts) showing the fresher data and letting minor lag pass unnoticed is usually fine, while for anything transactional (checkout pricing, inventory availability) it's often safer to re-verify against the write model or a stronger-consistency source at the point of commitment rather than trust either read model.
- What would change about this design if the team used a single shared database table with different indexes instead of genuinely different storage technologies for each read model?You'd lose the 'right tool for the job' benefit — the same storage engine may not be great at both full-text search and low-latency key lookups — but you'd gain simpler operations (one store to run) and, if updates happen in a single transaction, you could get all read models consistent with each other at the same instant instead of independently lagging.
It's like three different departments — sales, warehouse, and finance — each keeping their own copy of a spreadsheet updated from the same incoming order emails, but each department reads and updates their copy on its own schedule. Even though everyone's working from the same emails, at any given moment the three spreadsheets can show different totals, and cross-checking one department's numbers against another's can turn up a mismatch that isn't really an error, just a timing difference.
saying these in an interview costs you the question
- Thinks multiple read models are automatically kept consistent with each other because they come from 'the same events'
- Can't explain why one projector might lag behind another
- No mention of the operational cost of running N different storage technologies
- Assumes rebuilding one read model is as cheap/fast as rebuilding all of them together
- Doesn't consider that a request combining data from two read models can show an inconsistent combined result