Chapter 13 · Denormalization, Caching, and Derived Data
Documenting and Testing Denormalized Structures
Document and test denormalized structures with reconciliation checks, freshness SLAs, rebuild procedures, observability, and failure-mode validation.
Learning outcomes
Denormalized structures fail differently from normalized source tables. They can drift, lag, duplicate events, miss updates, and become unrebuildable. Production readiness therefore requires explicit documentation, reconciliation, automated tests, observability, and recovery procedures.
Create a denormalization contract for each derived structure.
Test synchronization, idempotency, freshness, and rebuild behavior.
Build reconciliation checks that detect drift.
Operate projections safely through failures and migrations.
Denormalization contract
NamePurposeSource of truthDerived fieldsSupported access patternsRefresh mechanismFreshness SLAIdempotency strategyReconciliation methodRebuild procedureOwnerExample contract
Name: WorkOrderListProjectionSource: WorkOrder + Asset + Customer + StatusFreshness: <= 5 secondsUpdate: outbox eventsRebuild: full backfillOwner: Service OperationsSupports: Q2, Q5Transformation tests
Given authoritative source rows, verify the projection computes correct labels, totals, null handling, default behavior, and historical semantics.
Idempotency tests
apply event 1001apply event 1001 againexpected: same final stateOut-of-order tests
version 42 arrivesversion 44 arrivesversion 43 arrives lateexpected: projection remains at 44Missing-event recovery
event 1001 appliedevent 1002 lostevent 1003 appliedVersion-gap detection, reconciliation, or a replayable source should expose or repair the gap.
Reconciliation
SELECT wo.work_order_idFROM work_order woLEFT JOIN ( SELECT work_order_id, SUM(quantity * charged_unit_price) AS actual_total FROM part_usage GROUP BY work_order_id) x ON x.work_order_id = wo.work_order_idWHERE wo.parts_total IS DISTINCT FROM x.actual_total;Sample-based reconciliation
For very large projections, continuous full comparison may be expensive. Use random samples, partition checksums, high-risk subsets, and periodic full audits.
Freshness monitoring
source latest event timestampprojection latest applied timestamplag = source - projectionAlert if lag exceeds the documented SLA.
Rebuild test
- create a new empty projection;
- backfill/replay source;
- verify counts and checksums;
- catch up live deltas;
- switch reads;
- retire old projection.
If a projection cannot be rebuilt deterministically, it is dangerously close to becoming a second source of truth.
Migration compatibility
Source schemas and projection schemas rarely change at exactly the same moment. Use compatible evolution so old and new consumers can overlap during rollout.
Backfill observability
- rows processed;
- rows remaining;
- throughput;
- error count;
- retry count;
- current lag;
- estimated completion based on measured throughput.
Failure tests
Simulate consumer restarts, duplicate messages, network timeout, cache eviction, database failover, and partial backfill failure. A design that only works in the happy path is not production-ready.
Stale-data behavior
When lag exceeds SLA, decide whether to serve stale data, fall back to authoritative queries, disable the feature, or block correctness-sensitive commands.
Ownership and alerts
Every derived structure should have an operational owner and actionable alerts. “Data team” or “backend team” is often too vague when an incident occurs.
WorkshopHub denormalization registry
| Structure | Freshness | Rebuildable? |
|---|---|---|
| WorkOrderListProjection | <=5 s | Yes |
| DailyWorkOrderSummary | <=1 min | Yes |
| SearchIndex | <=30 s | Yes |
| Invoice snapshot fields | Immutable | From invoice source/event |
Production-readiness checklist
Before launch
A new CustomerDashboardProjection is ready. What must be proven?
Review answer
Source ownership is documented; refresh is idempotent; out-of-order events are safe; freshness is monitored; reconciliation exists; full rebuild works; migrations are compatible; failure behavior is defined; and an operational owner is accountable.
Chapter 13 synthesis
Summary and next chapter
Chapter 13 showed how to denormalize without losing control of truth. You can justify duplication from evidence, assign ownership, build materialized aggregates, design caching layers, and test/reconcile derived data. Chapter 14 moves into time and structure: event history, effective dates, slowly changing operational data, recursive hierarchies, closure tables, and graph-like relationships.
References
- Martin Kleppmann, Designing Data-Intensive Applications.
- Martin Fowler, writings on CQRS, event sourcing, and caching.
- Ralph Kimball and Margy Ross, The Data Warehouse Toolkit.
- DBMS documentation for materialized views and replication.