← Back to list

You Don’t Need a Global Store, You Need Local Truth

Inside the architecture that stops your frontend from lying to itself.

Enrico Piovesan in Rethinking the Client · 2025-10-07 04:53 · 1 claps · 10.4 min read
#client-side-microservices #software-architecture #micro-frontends #mfe
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🏛️ · Architecture

You Don’t Need a Global Store, You Need Local Truth

Inside the architecture that stops your frontend from lying to itself.

Rethinking the Client: A New *Era of Modular, Performant Frontends” | **Part 20***

In the early days of the telegraph, information could only travel as fast as the line allowed. Messages sped through copper wires quickly, but the system was delicate. A single spark, a damp coil, or a tired operator could turn “CONFIRM PAYMENT RECEIVED” into “CONFER PAYMENT RECEIPT.” The difference was subtle but could cause significant confusion. By the 1850s, telegraph networks connected cities, banks, and governments across continents. However, trust in the messages was limited. There was no checksum or guarantee that the message in Boston matched the one sent from London. Operators created their own verification methods, such as small coded phrases added at the end of each message. These codes, like XR7, indicated that the message was complete and verified. Without such codes, the receiver could not be sure if the message was authentic or corrupted.

What emerged was an early form of distributed consistency. Each station held a part of the truth, but no single station had complete knowledge. Messages needed validation at both ends, forming a network that relied on mutual agreement rather than a central authority. Truth became something negotiated through protocols. Modern software faces similar challenges. Each component of a large frontend believes it understands what is real, but perceptions often differ. One service updates the cart, another the user profile, and a third the payment status, all assuming their data is current and correct. Like early telegraph lines, signals can be transmitted faster than they can be verified. As with those operators, we develop verification codes not to speed up processes, but to trust what we already possess.

In Client-Side Microservices Architecture (CSMA), this concept assumes a new form. Truth must be local first, verifiable through contracts, and shared via clear events instead of an invisible state. It echoes a lesson that telegraph engineers learned over a century ago: without verification, every message is a guess, and every shared truth could be a lie.

TL;DR

Global states promise simplicity but deliver confusion. What seems like a single source of truth quickly becomes a shared illusion, where services overwrite each other’s data and drift out of sync.

In Client-side Microservices Architecture (CSMA), each service owns its own state, communicates through events, and defines its truth through contracts. This local-first model prevents cascading errors, reduces coupling, and makes data flows observable and trustworthy.

Global truth creates dependency; distributed truth creates alignment. When every service speaks honestly within its own boundary, the system as a whole becomes clearer, safer, and easier to scale.

The Illusion of Global Truth

Every frontend project starts with a dream of simplicity. Teams envision a single location where data is stored, neatly shared across all modules. They call it a “global store,” a universal source of truth that promises order in a chaotic world. At first, it works perfectly. Components pull from the same state, updates feel synchronized, and developers can understand data flow at a glance. But as the application expands, that truth begins to drift. What once seemed like harmony turns into a quiet form of coupling. Every feature that writes to the global store changes how others read from it. Over time, the store becomes less of a truth engine and more of a rumor mill. Each service believes it knows what’s real, but no one knows who last changed the story.

Diagram: All services depend on the same mutable store. The data appears shared, but timing and ownership are undefined, creating hidden coupling

Diagram: All services depend on the same mutable store. The data appears shared, but timing and ownership are undefined, creating hidden coupling

In Client-side Microservices Architecture (CSMA), this illusion can be deadly. CSMA relies on independence, with each service controlling its own lifecycle, data, and events. A global store undermines this independence by creating hidden dependencies between services. A small change to a shared object can cause unpredictable side effects elsewhere in the system, rendering modularity fragile. To illustrate, imagine two services: one manages a user’s cart, and the other provides recommendations. Both rely on a shared user object stored globally. When the user logs out, the cart service clears the session immediately. However, the recommendation service, listening to the same global reference, doesn’t get the update in time. It continues to show personalized suggestions for a user who no longer exists.

That small delay is enough to erode trust in the system. The UI still appears correct, but beneath the surface, the truth has fractured.

// Both services rely on the same global state
GlobalState.user = { id: 42, loggedIn: true };

// User logs out
CartService.clear(); 
// RecommendationService still sees user.loggedIn = true

The issue isn’t just outdated data; it’s misplaced accountability. When everyone shares one version of the truth, no one truly owns it.

When Truth Drifts, The Hidden Cost of Shared State

The moment the global state begins to drift, the system slips into a quiet kind of chaos. It isn't the dramatic kind that destroys everything all at once, but the subtle decay that lurks behind green buildings and passing tests. A stale value here, a race condition there, and soon, different parts of the application start living in different versions of reality. In distributed frontends, drift occurs for the same reasons as in distributed systems: latency and ownership. Two services writing to the same store never do so simultaneously, even if it feels instant. One might optimistically update the user profile, while another continues processing an older snapshot. Both succeed locally, but globally, the truth has forked.

In Client-side Microservices Architecture (CSMA), these drifts become especially risky because services are designed to operate independently. Each microservice runs in its own thread or worker and responds to events asynchronously. When they share mutable global data, synchronization ceases to be an architectural feature and becomes a matter of luck.

// Service A updates user balance
GlobalState.user.balance = 100; 

// Service B, running in a different thread, reads the old value
processPayment(GlobalState.user.balance); // Uses outdated data

The result is not just inconsistency; it's misinformation. The UI might display the correct total while background services run on outdated logic. Debugging these cases is notoriously difficult because it is impossible to replicate the exact sequence of events that caused them. The system didn't crash; it simply lied. Some teams attempt to address this issue by introducing state managers that coordinate updates or by enforcing strict update flows. However, these solutions only mask the issue. They centralize coordination instead of sharing responsibility, turning the runtime into a bureaucratic bottleneck where every service must wait for permission to report the truth.

True scalability doesn't come from controlling how everyone speaks; it comes from enabling each service to speak for itself.

Scoped and Local-First State

If the global state is a rumor mill, the local-first state is a personal notebook. Each service maintains its own version of the facts, updates them responsibly, and syncs only when necessary. The aim is not to have a single, universal truth, but rather many consistent local truths that eventually align through clear communication.

Diagram: Each service owns its local state. Synchronization happens through emitted events, not direct access.

Diagram: Each service owns its local state. Synchronization happens through emitted events, not direct access.

In Client-side Microservices Architecture (CSMA), each service manages its own data. It handles fetching, storing, and maintaining that data within its own lifecycle. Other services can listen to its events, but they never directly modify its internal state. This creates a system where ownership is clear and side effects are predictable. Imagine a runtime comprising multiple workers, each hosting an independent microservice. One might track user sessions, another might manage preferences, and another might handle notifications. They communicate through events rather than shared memory. When the session service emits a “user.loggedOut” event, other services respond by clearing their caches or resetting internal values. No one alters anyone else’s data; instead, the system remains synchronized through shared signals rather than shared variables.

// SessionService.ts
emit("user.loggedOut");

// PreferenceService.ts
on("user.loggedOut", () => clearPreferences());

This local-first model might seem redundant at first. Why keep multiple copies of similar data when a single global store could hold everything? The answer is resilience. Local-first systems can handle latency, disconnections, and partial failures without failing completely. A temporary delay between two services doesn't cause data corruption, but rather a short divergence that corrects itself once the events are processed. In practice, this pattern creates a runtime as a network of small, truthful agents. Each one sees only what it owns, and every update becomes a form of communication rather than mutation. The more autonomous each service becomes, the more honest the system stays as a whole.

Truth as a Contract

Truth in software isn't about having a single perfect value. It's about having an agreed-upon definition of what that value means. Without that consensus, even the most synchronized systems can drift apart. This is where contracts become critical, transforming implicit expectations into explicit promises.

In Client-side Microservices Architecture (CSMA), each service defines its own contract. This contract outlines the data it owns, the events it emits, and how others can subscribe to them. Once these definitions are formalized, the state is no longer a matter of shared memory but of structured communication. Truth becomes something that can be versioned, validated, and verified. For example, a “UserProfileService” might emit events like “profile.updated” or “session.expired.” Other services that rely on this information subscribe to those events and react accordingly. The key difference is that they never assume the internal structure or timing of those updates. Their relationship is entirely defined by the contract, not by the implementation details.

// user-profile.contract.ts
interface UserProfileUpdated {
  event: "profile.updated";
  payload: { name: string; email: string };
}

This approach converts the state from a hidden side effect into a clear part of the architecture. Teams can reason about truth in the same way they reason about APIs: through predictable inputs and outputs, rather than shared assumptions. When truth is considered a contract, it becomes more stable and reliable. A service can change its internals freely as long as it honors the contract that others rely on. The system remains honest even as it evolves because each part knows what truth it is responsible for. In a modular frontend, this change is subtle but revolutionary. It replaces global truth with negotiated truth. Instead of pretending that one store can define reality for everyone, CSMA allows each service to declare what it knows and trusts the runtime to coordinate those pieces into a unified whole.

Diagram: Contracts define what “truth” means in the system. Services publish and subscribe using event definitions, rather than relying on shared variables.

Diagram: Contracts define what “truth” means in the system. Services publish and subscribe using event definitions, rather than relying on shared variables.

Putting It All Together

Once the illusion of a single global truth dissipates, a more dependable pattern emerges. Each service in the runtime acts as a small observer of the world, responsible for its own truth but aware of others through contracts and events. This is what makes a Client-side Microservices Architecture (CSMA) resilient. It accepts that truth is distributed and designs around this fact rather than fighting it. When services own their own state, coordination happens through intent, not mutation. A payment module does not need to access a user's store to check their balance; it listens for a “balance.updated” event from the user service and reacts when it arrives. A product gallery does not need to share a global loading flag; it emits “loading.started” and “loading.finished,” allowing other services to respond within their own scope. The result is a runtime where truth is transmitted via messages rather than stored in memory.

// PaymentService.ts
on("balance.updated", (balance) => process(balance));

This design restores predictability to the system. Instead of troubleshooting hidden side effects, developers can follow the flow of truth as it moves between services. The event history itself becomes a story of what the system believes at each moment. The change also boosts team independence. Each group can build, test, and deploy its service independently, knowing it won't inadvertently disrupt another’s internal state. The need for coordination decreases because contracts, not just conventions, determine how truth flows.

Diagram: Truth flows through events across independent services. Each one owns its data but remains aligned through communication, rather than shared memory.

Diagram: Truth flows through events across independent services. Each one owns its data but remains aligned through communication, rather than shared memory.

When truth has boundaries, the system becomes self-correcting. One service can fail, restart, or refresh without affecting the others. Data is no longer fragile to protect but is alive to synchronize. That is the core of truth boundaries. They transform the front end from a single nervous system into a federation of minds that think together without thinking exactly alike.

Takeaways

  1. Global state is not a single source of truth; it is a single point of distortion. What begins as convenience quickly becomes a hidden coupling. Shared memory hides ownership, timing, and intent, eroding modularity from within.
  2. Truth should be local first and shared intentionally. Each service in a Client-side Microservices Architecture (CSMA) must own its own data and synchronize through events. This keeps boundaries explicit and failures contained.
  3. Contracts define truth better than variables do. When state changes are expressed through versioned contracts, they can be verified, tested, and reasoned about. Agreement replaces assumption.
  4. Event-driven communication turns drift into dialogue. A system built on messages rather than mutations can tolerate latency, maintain autonomy, and stay coherent over time.
  5. Truth boundaries are as important as trust boundaries. Security prevents services from interfering with each other. Truth keeps them from lying to each other. Together, they make modular frontends honest, scalable, and resilient.

Coming Up Next

Truth boundaries help teams build honest systems, but honesty alone does not make an architecture maintainable. The next challenge lies in what happens when logic leaks into places it does not belong. In Part 20: “UI Without Logic, Designing for Maintainability,” the focus shifts from runtime behavior to design discipline. It will explore how separating rendering from reasoning enables modular frontends to evolve quickly, remain safe to refactor, and scale more easily across teams.

(Monday)

(Monday)

🤔 Wait, Isn’t This Just Micro Frontends?

Not quite.

Micro frontends focus on splitting up the UI, allowing different teams to own and manage various parts of the visual interface, which are often deployed separately.

**Client-side Microservices Architecture (CSMA) is different: it’s not about the UI at all. It’s about breaking up business logic components, such as state management, calculations, workflows, and side effects, into independent, testable services** that run inside the client app.

Think of it as giving your frontend the same internal structure and discipline as a backend system, without turning your components into dumping grounds for logic.

You can use CSMA with or without micro frontends. They solve different problems, and they complement each other well.

🧱 Found this helpful?

If this post helped clarify how to think about modular frontend logic, give it a 👏 or share it with someone buried under spaghetti code.

📚 Following the series? This article is part of the ongoing series: Client-side Microservices: Rethinking Frontend Architecture

[embed]Rethinking the Client Say goodbye to bloated frontend codebases. This series digs into how to make client-side applications leaner, more…medium.com

Each post breaks down how to bring structure, scalability, and sanity to modern frontend development, one small service at a time.


메타데이터
post_id
4cf50c92a63a
slug
you-dont-need-a-global-store-you-need-local-truth-4cf50c92a63a
url
https://medium.com/rethinking-the-client-a-new-era-of-modular/you-dont-need-a-global-store-you-need-local-truth-4cf50c92a63a
canonical_url
https://medium.com/rethinking-the-client-a-new-era-of-modular/you-dont-need-a-global-store-you-need-local-truth-4cf50c92a63a
author_url
https://medium.com/@enricopiovesan
status
ok
fetched_at
2026-06-24 11:06:28