The Single Source of Truth Is Not a Database, It Is a Design Discipline

tfc

Hatched by tfc

Jun 03, 2026

10 min read

86%

0

The real problem is not storage, it is agreement

What do a React component tree and a distributed enterprise system have in common? At first glance, almost nothing. One lives inside a user interface, the other spans services, queues, APIs, and external systems. Yet both are haunted by the same question: when two things must change together, where should the truth live?

That question sounds technical, but it is really architectural. It is about deciding whether consistency should be created by duplication, or by deliberate ownership. In a small interface, the answer is often obvious. If two components need the same state, you do not copy the value into both and hope they stay in sync. You lift it up to the closest common parent and let that parent own the truth. In a distributed system, the shape is different, but the challenge is identical: events, messages, and integrations must agree on what happened, when it happened, and which component is responsible for translating it.

The deeper tension is this: systems become brittle when they confuse convenience with ownership. Duplicating state feels easy in the moment. It reduces immediate wiring. But the price is subtle, and it compounds. Every extra copy creates another place for drift, another chance for disagreement, another debugging session where the question is not “what happened?” but “which version is right?”


Why duplication feels good, until it does not

Duplication is seductive because it buys short term relief. In a UI, two components can each keep their own copy of a value and appear to work. In a distributed environment, one service can quietly mirror another system’s data and seem faster, simpler, or more decoupled. But these are temporary victories. The moment the shared reality changes, the hidden cost arrives: stale values, race conditions, awkward synchronization code, and logic scattered across too many places.

This is why the principle of a single source of truth is more than a tidy engineering slogan. It is a way of preventing agreement from becoming accidental. A single source of truth does not mean all data lives in one monolith. It means each piece of state has a clearly designated owner. That owner is the place where updates happen, where invariants are enforced, and where the system can answer, decisively, “what is true right now?”

A useful mental model is to think of shared state like a company decision. If three managers each keep their own unofficial version of the policy, confusion spreads. The team does not need more copies of the policy. It needs one place where policy is set, then distributed from there. The same logic applies whether the “team” is a pair of sibling components or a network of services.

The cost of duplication is not storage. The cost of duplication is disagreement.

That is the first bridge between interface design and system architecture. The issue is not simply how data moves. The issue is how truth is preserved while it moves.


Lifting state up and routing events are the same idea in different clothes

In user interfaces, lifting state up is the act of moving shared state to the nearest common parent and passing it down as props. That parent becomes the owner, the coordinator, the source of truth. This is elegant because it removes ambiguity. The children do not negotiate with each other. They receive the same value and ask the parent to update it when necessary.

In distributed systems, patterns such as message routing, transformation, filtering, and mediation play a similar role. The point of an integration pattern is not merely to move data from place to place. It is to preserve meaning while adapting to different boundaries, different protocols, and different responsibilities. Patterns are technology agnostic precisely because the challenge is larger than any implementation. A queue, pipe, or event bus is not the insight. The insight is the structural decision about who owns what, and how the rest of the system should stay aligned.

Consider a concrete example. Suppose an order service emits a message when an order is placed. A billing system needs that event, but it does not need the entire order payload. A shipping system needs a slightly different shape of the same event. A fraud system needs to inspect only selected fields and maybe enrich them with external data. If every consumer directly couples itself to the producer’s internal structure, the producer becomes a gravitational center of accidental complexity. Every change ripples everywhere.

A better design introduces an architectural boundary that behaves like lifting state up. One component or integration point becomes responsible for owning the canonical event, while intermediary patterns transform it into the forms other systems require. The point is not centralization for its own sake. The point is explicit ownership plus controlled projection. One source of truth, many views.

This is where the analogy gets interesting. In React, props are a deliberate projection of state into children. In enterprise integration, translated events or routed messages are deliberate projections into consumers. In both cases, the architecture is healthiest when the owner is clear and the dependents receive only what they need.


The hidden design choice is not data flow, it is authority

Most teams talk about data flow when they should be talking about authority. Data flow asks where bytes travel. Authority asks who is allowed to define reality. Those are not the same question.

A component tree works because the parent has authority over the shared state. Children can request changes, but they do not define the canonical value independently. That separation prevents conflicting edits. The same principle matters in event-driven architectures. The source system should be authoritative for its domain facts, while downstream systems should treat those facts as inputs, not as opportunities to rewrite history.

This distinction matters especially when systems need to integrate with third-party services. Third parties are notorious for having their own schemas, timing, and semantics. A naive design lets each consumer fight the external system directly, multiplying complexity. A better design creates a controlled integration layer that absorbs the mismatch and presents a stable internal contract. The integration layer is not just plumbing. It is an act of governance.

Think of it like a translator at a meeting. The translator is not the speaker, but the translator decides how meaning is preserved across languages. If every attendee tries to translate for themselves, the meeting collapses into noise. Likewise, if every subsystem independently interprets external events, your architecture stops having a shared reality.

This reveals a practical rule: the more heterogeneous the system, the more valuable explicit ownership becomes. Heterogeneity increases the chance that the same fact will be represented differently. Without a canonical owner and controlled adapters, “the same fact” gradually stops being the same.


Patterns are not recipes, they are answers to recurring coordination problems

There is a common misunderstanding about patterns. People treat them like templates to copy. But patterns matter because they encode recurring answers to recurring questions. They are not blueprints in the sense of rigid structure. They are blueprints in the sense of proven coordination logic.

This is especially true for enterprise integration patterns. They exist because distributed systems repeatedly encounter the same problems: how to connect incompatible systems, how to isolate failures, how to transform messages, how to filter noise, how to manage bursts, how to preserve order or tolerate its absence. The pattern is the decision rule. The implementation is the local expression of that rule.

React’s “lift state up” is also a pattern in this sense. It is not a trick for a specific component. It is a reusable answer to the recurring problem of shared state. If two children must stay synchronized, the parent owns the truth. Everything else flows from that choice.

The parallel is powerful because both cases push against a common human instinct: to solve coordination by letting each participant manage its own little world. That seems decentralized, but it often creates a more expensive form of centralization later, when hidden dependencies force manual reconciliation. The system pretends to be distributed until a bug proves otherwise.

A better mental model is to separate systems into three layers:

  1. Canonical ownership: where truth is defined.
  2. Adaptation: how truth is reshaped for different participants.
  3. Consumption: how participants use the projected version without redefining it.

In UI architecture, this looks like a parent owning state, children receiving props, and callbacks requesting changes. In distributed integration, it looks like a source of record, a transformation or routing layer, and downstream services that react to the message. Different tools, same architecture of truth.


What this means in practice: design for one truth, many views

The most useful lesson here is not “centralize everything.” That would be a mistake. A system with one giant blob of truth is usually worse than a system with many local truths. The goal is narrower and more precise: each fact should have one owner, but many carefully designed views.

This distinction solves a lot of architecture debates. Should a component own its own copy of some input? Only if that copy is truly local and does not need to stay synchronized with anything else. Should a service duplicate data from another system? Only if the duplication is clearly a derived cache, not a competing authority. Should an integration layer transform messages? Yes, if the transformation preserves a stable internal contract and prevents every consumer from reinventing the same adapter logic.

A practical test is to ask three questions:

  • Who is the authoritative owner of this fact?
  • Who is allowed to change it?
  • Who merely needs to observe or adapt it?

If you cannot answer those questions cleanly, your architecture is probably blending ownership with convenience. That blend is where bugs breed.

Here is a simple example. Imagine a checkout page with a cart summary and a payment panel. If both components store their own copy of the cart total, one may show $48 while the other shows $52 after a discount changes. The fix is not to synchronize them ad hoc. The fix is to place the cart state in the nearest owner that can represent the canonical total, then let each component render from that shared truth.

Now imagine a larger system. The order service, inventory service, and shipping service all need order events. If each service independently pulls and reshapes raw order data from the database, the contract is brittle and repeated. If instead there is a stable event model or integration layer that publishes a canonical order fact and provides appropriate transformations, the system becomes easier to evolve. The same principle holds, only the scale differs.

Good architecture does not eliminate differences. It controls where differences are allowed to appear.

That is the real gift of these ideas together. They do not merely tell you where to put state or how to wire services. They tell you how to think about truth under coordination pressure.


Key Takeaways

  1. Assign one owner per fact. Do not ask where a value can live. Ask who is responsible for its truth.
  2. Use duplication only for derived views or caches. If two places must stay in sync, duplication is usually a smell, not a solution.
  3. Separate canonical data from adapted data. One source can feed many representations, but those representations should not compete to define reality.
  4. Treat integration layers as governance layers. Their job is not just moving data, but preserving meaning across boundaries.
  5. When debugging, look for disagreement before looking for code. Many bugs are really symptoms of unclear ownership.

The architecture of truth

The most interesting thing about both component state and distributed integration is that they force the same discipline at different scales. In a small UI, truth must be centralized enough to avoid sibling rivalry. In a large enterprise system, truth must be centralized enough to avoid semantic drift, but distributed enough to remain adaptable.

That balance is what mature architecture really looks like. Not a pile of shared copies, and not a monolith that swallows everything. Instead, a carefully designed map of ownership, projection, and transformation. The owner defines the truth. The rest of the system consumes it through well chosen boundaries.

So the next time you are deciding whether to duplicate state, replicate an event, or let two parts of a system manage themselves independently, ask a deeper question: am I designing for convenience, or for agreement? Convenience is fast today. Agreement is what keeps the system intelligible tomorrow.

In the end, the single source of truth is not a database or a component. It is a design discipline, a refusal to let reality be defined in more than one place unless the differences are intentional. That is what makes a system understandable. That is what makes it evolvable. And that is what makes it worth trusting.

Sources

← Back to Library

Hatch New Ideas with Glasp AI 🐣

Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)

Start Hatching 🐣