“The database is the source of truth” can settle an argument in a meeting and leave the design unresolved. The database may own a fact while a cache, search index, queue, and browser each hold a different version of it.
In a document-sharing product, a relational database stores access grants. A cache speeds up permission checks. A search index helps people find documents. A queue delivers indexing work. The browser keeps an optimistic local copy. An owner revokes access. Which component answers the next request?
The database may be authoritative for the grant. That answer does not yet tell us when the cache stops serving an old grant, whether search still shows a title, what a queued index job may publish, or what the browser should display while the write is in flight. Source of truth is a useful phrase only when it leads to contracts for those other copies.
Name the Decision, Then Its Authority
There is rarely one source of truth for an entire product. There is an authority for each kind of decision. The document database may own access grants; object storage may own file bytes; a payment provider may own the outcome of an external charge. The same system can have several authoritative components without having an ambiguous decision.
For a decision that matters to a user, the team should be able to answer five questions:
- What fact are we asking for?
- Which component may commit that fact?
- What event makes the commitment visible?
- Which copies may lag, and by how much?
- What happens if a copy disagrees or cannot be reached?
In the document example, “Can this user open this document?” is a different decision from “Which documents should appear in a search result?” Search can be approximate and delayed. Access control should not inherit that delay merely because a search index is convenient to query.
Copies Are Products of a Contract
A cache is a copy with an expiry and invalidation policy. A search index is a projection with a refresh policy. A queue is a record of work to perform, not usually proof that the work has happened. A browser view is a temporary interpretation of server state. Treating any of them as the answer without naming its policy creates accidental consistency rules.
flowchart LR
user[User action] --> command[Validated command]
command --> truth[(Authoritative state)]
truth --> event[Durable change intent]
event --> index[Search projection]
event --> cache[Cache invalidation]
truth --> read[Authoritative check]
index --> discovery[Discovery view]
cache --> fast[Fast read]
The command commits authoritative state before projection and cache work follows. If the product promises that a revocation takes effect before the write returns, the fast read path must honour that promise. A TTL permits the old grant until expiry. Write-time invalidation removes it as part of the revocation path.
Time Changes the Answer
“The database is correct” is incomplete when a write and read race. Consider a grant revocation:
- At t0, a cache contains
allowed = true. - At t1, the owner submits a revocation.
- At t2, the database commits
allowed = false. - At t3, the API acknowledges success.
- At t4, another request checks access.
At t3, the owner has been told the revocation succeeded. The request at t4 must see the new permission. That requires write-time invalidation, a version check, a source read, or another policy with the same effect. The write path also needs a response to invalidation failure: fail the operation, retry it, or force subsequent reads to the source.
Time matters in the other direction too. A browser may show an optimistic edit before the database commits. That can be a good interaction, provided the UI can roll it back and does not label tentative state as durable fact.
Durability Is Different From Delivery
Queues often confuse this discussion. Enqueueing an event can mean “we intend to update the index.” It does not mean the index is current. Receiving the event does not mean the update committed. A worker can crash after writing the projection but before acknowledging the message.
That is why derived work needs idempotency and a recoverable relationship to its source. A stable event identity, a monotonic version, or a source cursor can let a worker decide whether it has already applied a change. The correct mechanism depends on the projection. The design question is consistent: how does a replay preserve the authority of the committed state?
Who Gets to Decide?
| Component | May decide | Must not silently decide |
|---|---|---|
| Transactional store | Committed domain facts it owns | External provider outcomes it has not reconciled |
| Cache | Whether a known answer is fresh enough to reuse | A new authoritative fact |
| Search index | Which candidates are discoverable | Final permission to open a protected result |
| Queue | Which work should be attempted | Whether that work completed |
| Client state | What to show optimistically | What the server has durably accepted |
The database can remain authoritative while caches and indexes make most reads fast. Each derived component needs a precise promise about freshness and failure.
When those promises are explicit, performance work becomes safer. We can add caches, indexes, and asynchronous projections while keeping the same answer to the decisions users care about. The Cache Correctness Lab and Data Store Decision Lab let you change the policies and inspect the consequences.