Part II - Composable Frontend Architecture

- Published on

Part II - Composable Frontend Architecture
A checkout page needs a small change: let customers choose a pickup location instead of entering a shipping address.
The form changes. So does validation. The order summary needs a different delivery estimate. The mobile layout needs room for a location picker. A support tool needs the same ordering behavior, but it has a different interface and different permissions.
What looked like a UI change now reaches into routing, business rules, data access, and shared state. Every part knows something about every other part. Moving the form into its own component does little to change that.
This is the problem I want to address with composable frontend architecture: how do we give these responsibilities clear boundaries so a change can stay understandable?
The answer starts with deciding what each part owns, what it needs from its neighbors, and what it promises in return. The chapters in this section give those boundaries names and explore how to implement them. You do not need to learn all the names before the architecture becomes useful.
Return to the main table of contents.
A Component Boundary Is Only the Beginning
A component can be reusable and still be tightly coupled to the application around it.
Imagine a pickup selector that reads the current customer from a global store, fetches locations, decides which locations are eligible, updates the cart, and redirects after selection. Its markup may be neatly contained in one file. Its behavior depends on half the application.
To use it in the support tool, we would have to reproduce those dependencies or add enough conditionals to accommodate a second environment. The component boundary made the code easier to find. It did not make the capability easier to move.
A more useful boundary lets the selector receive available locations and report a selection. The checkout workflow coordinates the cart update. The service responsible for the order validates whether pickup is allowed. The page decides where the selector belongs.
Those responsibilities can still live in one application and ship in one bundle. Separate ownership does not require separate deployment. Independent deployment becomes useful when teams need that freedom and can support the compatibility and operational work it introduces.
Start With Four Questions
Before choosing a framework or creating packages, ask four questions about the feature in front of you.
What belongs on the page? The checkout needs an address form or a pickup selector, an order summary, and a way to continue. Composition decides which modules appear and how they fit together. It should be possible to rearrange those modules without rewriting the order workflow.
What happens when the user acts? Choosing a location may require updating the cart, checking availability, and refreshing the delivery estimate. That workflow needs an owner outside the selector's rendering code. Otherwise every interface that offers pickup has to reconstruct the sequence.
How should the experience adapt? A narrow screen may need a stacked layout. A different brand may supply different design tokens. A customer may need reduced motion or larger text. These adaptations should preserve the meaning of the checkout and the information needed to complete it.
How does the user complete the interaction? Opening the picker, moving through results, selecting a location, and recovering from an error form a coherent interaction. Focus management and feedback belong in that design from the beginning.
In this series, those four concerns are called the Dynamic Interface Mesh, Composable Execution Layer, Adaptive Presentation Core, and Modular Interaction Layer. The names are shorthand for responsibilities. The useful test is whether you can explain a change without tracing through all four at once.
Follow One Checkout Through the System
Suppose a customer opens checkout and chooses pickup. This is an illustrative design; an existing application may combine several of these responsibilities in the same framework or module.
The application host resolves the checkout route, establishes shared context such as locale and session state, and loads the checkout experience. If loading fails, the host needs a recovery path. This is the Composable Runtime Shell: the part that makes a feature available within the application and manages its lifecycle.
Once checkout is available, composition places the fulfillment controls beside the order summary. On a smaller screen, the presentation rules adapt that arrangement while preserving a sensible reading and focus order. Composition determines what belongs together; presentation determines how that arrangement works in the current context.
The customer opens the pickup selector. Its interaction behavior manages focus, selection, loading feedback, and any error message. A pointer click and keyboard activation should lead to the same intended action. The Universal Interaction Framework examines that input-to-intent boundary. Native controls already handle many of these semantics; an abstraction should preserve that behavior and cover the cases the product actually needs.
Selecting a location asks the checkout workflow to update fulfillment. The workflow coordinates the request and represents the pending, successful, or failed result. The authoritative service checks eligibility and returns the updated cart. Frontend logic can guide the customer, but the service still has to enforce the rules when it accepts the change.
After success, the order summary needs the new totals and pickup details. Within a closely connected feature, shared state or a direct callback may be enough. When independently composed modules need to react to the change, an explicit event contract can keep the selector from reaching into the summary's internals. The Event-Driven Component Network explores that communication boundary. It does not make every local state change a reason to publish an event.
Two further responsibilities become relevant in more demanding products. If a service or content system supplies the page configuration, the Data-Driven Presentation Layer defines how that data maps to supported UI. If the experience spans browser and server execution, the Cross-Surface Execution Engine examines where work belongs and what can cross that boundary.
These are cooperating responsibilities, not nine mandatory stages in a request. A checkout can use the first few boundaries well without needing a remote layout schema or a general-purpose execution engine.
Make the Handoffs Explicit
The difficult work is usually between the boxes in an architecture diagram.
When the selector requests a pickup change, what does it send? Does the response contain the updated cart or only an acknowledgment? Can the customer change the selection while the first request is pending? If requests finish out of order, which result should the UI show?
A contract needs to answer those questions. A function signature is a start, but it does not describe the whole behavior.
For this checkout, the workflow might accept a cart identifier and location identifier, expose a pending state, and return a confirmed cart or a defined failure. The UI can then show progress, preserve the previous confirmed selection on failure, and offer a useful retry. Handling repeated submissions also needs an explicit policy at the service boundary; disabling a button alone cannot provide that guarantee.
Ownership matters just as much. The selector owns its open state and keyboard behavior. The checkout workflow owns the progress of the fulfillment change. The order service owns the accepted cart. Copying all three into a global store without distinguishing them makes disagreement harder to diagnose.
A boundary is useful when both sides can explain what remains true during failure. That is what allows a team to change one implementation without rediscovering the entire system.
Read the Chapters Around the Problem You Have
For a first pass, start with the runtime shell, composition, and execution chapters. They explain how the application hosts a feature, places it on a page, and coordinates its behavior. Then read presentation and interaction to see how those boundaries become a coherent experience for the person using it.
The remaining chapters address more specific pressures: multiple input methods, communication between independent modules, configured interfaces, and execution across environments. Read them when those pressures are relevant to your product.
| When you need to understand… | Read |
|---|---|
| Who loads the feature, supplies shared context, and handles its lifecycle | Composable Runtime Shell (CRS) |
| How modules are selected and assembled into a page | Dynamic Interface Mesh (DIM) |
| Where workflows, service calls, and operation state belong | Composable Execution Layer (CEL) |
| How layout and styling adapt across devices, brands, and user preferences | Adaptive Presentation Core (APC) |
| How reusable controls and interaction flows manage focus, feedback, and local behavior | Modular Interaction Layer (MIL) |
| How different input methods express the same intended action | Universal Interaction Framework (UIF) |
| How independently composed modules communicate through explicit events | Event-Driven Component Network (ECN) |
| How schemas and service data become validated UI configuration | Data-Driven Presentation Layer (DPL) |
| How execution responsibilities are divided across browser, server, edge, and embedded environments | Cross-Surface Execution Engine (CSEE) |
Accessibility runs through several of these chapters. Presentation can respect user preferences, interaction can preserve focus and keyboard behavior, and composition can maintain a meaningful page order. Each owner needs to preserve those properties when changing its part of the experience.
Go Deeper Into Multi-Team Delivery
Two implementation walkthroughs extend the core chapters using an illustrative accounting platform. Chapter 4: Dynamic Interface Mesh follows module ownership, versioned manifests, tenant variation, previews, and rollout. Chapter 5: Composable Execution Layer follows service contracts, scoped injection, asynchronous cleanup, and server rendering.
Read these after the introductory composition and execution chapters when you need to support several teams or hosts. They examine the operational details behind the same boundaries.
Introduce the Boundary Where Change Hurts
There is no need to build the entire model before improving an existing frontend.
Start with a feature whose coupling is already costing you time. In the checkout example, that might mean extracting the fulfillment workflow from the location selector. Give the workflow an explicit input, result, and failure contract. Keep it in the existing application while you learn whether the separation helps.
Then make a real change. Add pickup to the support tool. Change the mobile arrangement. Make an unavailable location return a recoverable error. Observe which parts need to change together and whether that coupling reflects a real dependency or an accidental one.
If both surfaces can use the workflow while keeping their own presentation, the boundary has earned its place. If every change still requires coordinated edits across all the new packages, the extra structure has not yet solved the problem.
A small product may need only a clear division between UI, workflow, and service access. A product family with independently delivered modules may need stronger runtime contracts, event compatibility, and failure isolation. The architecture should grow with the responsibilities the team can identify and own.
The reader should be able to follow a user action through the system. The engineer should be able to explain where a change belongs. That is the standard I would use before adding another layer.