Problem
Reads that join many tables or aggregate large datasets are slow and costly, and they repeat the same work for every request. The read path is far more frequent than the write path but pays for the complexity of the normalized model.
Also known as: precomputed-views, read-optimized-models
Compute or duplicate data at write time so that reads become simple lookups.
Reads that join many tables or aggregate large datasets are slow and costly, and they repeat the same work for every request. The read path is far more frequent than the write path but pays for the complexity of the normalized model.
Precomputation moves work from read time to write time, or to a scheduled job. Denormalization is the storage side: keeping redundant copies of data so queries need no joins.
order placed ──> orders table (source of truth)
│
└─ event/CDC ──> updater ──> customer_totals(customer_id, order_count, revenue)
product_page_doc(product_id, price, rating, stock)
An updater consumes changes and maintains the read model. Increments should be idempotent: store the last processed event id or recompute from the source for the affected key. A nightly or weekly rebuild corrects any accumulated drift.
Choose the approach by freshness need. In-transaction updates give immediate consistency but couple write latency to the number of derived copies. Change-data-capture pipelines add seconds of delay with no impact on the writer. Batch jobs are cheapest and suit reports and rankings.
Precompute only what is hot. Measure the top queries by cost times frequency, then build projections for those. Everything else can stay on the normalized model, where flexibility is worth more than speed.
Extra storage and compute for pipelines
Medium: backfills and reconciliation
Medium: multiple representations of truth
Derived data drifts after a missed update event
Backfill job overloads the primary database
Schema change in the source silently breaks the projection