Chapter 13 · Denormalization, Caching, and Derived Data
Caching Layers and Source-of-Truth Design
Design caching layers around clear source-of-truth ownership, freshness contracts, invalidation, read-through/write-through patterns, and failure behavior.
Learning outcomes
A cache is a denormalized copy optimized for faster access. Its value depends on a clear source of truth, a freshness contract, and predictable behavior when the cache is stale, unavailable, or inconsistent.
Define the authoritative source behind each cache entry.
Compare cache-aside, read-through, write-through, and write-behind patterns.
Design invalidation and TTL policies from business freshness requirements.
Prevent caches from becoming accidental primary databases.
Cache as derived state
Unless architecture explicitly says otherwise, the relational database remains authoritative.
Cache-aside
read cacheif hit: returnif miss: read database populate cache returnThe application owns cache population and invalidation.
Write invalidation
BEGINUPDATE work_order ...COMMITDELETE workorder:84217The next read repopulates the key. The risk is the window where database commit succeeds but invalidation fails.
Read-through cache
With read-through caching, the cache layer itself loads missing data. This simplifies callers but moves source-fetch logic into the caching abstraction. The source of truth is still separate.
Write-through cache
A write-through layer updates cache and backing store as one logical path. It can keep cache fresh but increases coupling and must define what happens if either write fails.
Write-behind cache
Writes reach cache first and persist later. This can reduce latency, but now cache durability and queue recovery affect correctness.
Do not use write-behind casually when the relational database is intended to be the durable authoritative store.
TTL-based freshness
WorkOrderStatus cache: 10 minCustomer profile summary: 60 sDashboard aggregate: 30 sInventory decision: bypass cache / validate sourceTTL should be a business decision, not a random number.
Event-driven invalidation
Versioned cache values
{ "version": 42, "payload": ...}A consumer can reject a cache value that is older than a required source version.
Cache key scope
tenant:17:workorder:84217Explicit tenant scope prevents accidental collisions and makes invalidation/auditing easier.
Negative caching
Caching “not found” reduces repeated misses, but use a short TTL if the entity might be created shortly afterward.
Cache stampede
If a hot key expires, thousands of requests can hit the database simultaneously. Mitigations include request coalescing, staggered TTLs, background refresh, and a lock around regeneration.
Cache penetration
Repeated queries for nonexistent IDs can bypass ordinary caches. Negative caching or validation can protect the backing database.
Eviction must not break correctness
A cache can discard entries under memory pressure. If losing a cache entry loses business data, it is not merely a cache.
Cache invalidation patterns
- delete-on-write;
- update-on-write;
- TTL expiration;
- event-driven invalidation;
- versioned namespace/key rotation.
Stale-read behavior
Define what the application should do when cache lag is too high:
- serve stale data with a marker;
- fall back to source;
- block the action;
- disable the feature.
Source-of-truth hierarchy
Each lower layer should be reconstructible from the layer above.
WorkshopHub cache candidates
| Data | Suitability | Reason |
|---|---|---|
| Status reference | Excellent | Small, slow-changing |
| Customer summary | Good | Readable with invalidation |
| Inventory availability | Risky for correctness | Fast-changing and contention-sensitive |
| Historical dashboard | Good | Staleness often tolerable |
Practice: cache or not?
Inventory decision
Can WorkshopHub use a 60-second cached inventory count to decide whether to commit a PartUsage transaction?
Review answer
No, not for correctness. The transaction must validate authoritative current stock through a concurrency-safe source. A cache can support display hints, but not the final commit invariant.
Summary and next lesson
Caches are derived read structures. Their safety depends on source-of-truth ownership, invalidation, TTL, versioning, and failure behavior. The final lesson shows how to document, test, reconcile, and operate denormalized structures systematically.
References
- Martin Kleppmann, Designing Data-Intensive Applications.
- Redis documentation on caching patterns.
- Martin Fowler, writings on caching and CQRS.