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.

Beginner70–100 minutesCaching + source of truthLast reviewed: August 2026

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.

01

Define the authoritative source behind each cache entry.

02

Compare cache-aside, read-through, write-through, and write-behind patterns.

03

Design invalidation and TTL policies from business freshness requirements.

04

Prevent caches from becoming accidental primary databases.

Cache as derived state

Unless architecture explicitly says otherwise, the relational database remains authoritative.

Cache-aside

python · example
read cacheif hit:    returnif miss:    read database    populate cache    return

The application owns cache population and invalidation.

Write invalidation

sql · example
BEGINUPDATE work_order ...COMMITDELETE workorder:84217

The 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.

Caution

Do not use write-behind casually when the relational database is intended to be the durable authoritative store.

TTL-based freshness

model · example
WorkOrderStatus cache: 10 minCustomer profile summary: 60 sDashboard aggregate: 30 sInventory decision: bypass cache / validate source

TTL should be a business decision, not a random number.

Event-driven invalidation

Versioned cache values

json · document
{  "version": 42,  "payload": ...}

A consumer can reject a cache value that is older than a required source version.

Cache key scope

model · example
tenant:17:workorder:84217

Explicit 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

DataSuitabilityReason
Status referenceExcellentSmall, slow-changing
Customer summaryGoodReadable with invalidation
Inventory availabilityRisky for correctnessFast-changing and contention-sensitive
Historical dashboardGoodStaleness 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.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.