the-composable-frontend-architecture

Composable Execution Layer (CLS)

By Everett Quebral
Picture of the author
Published on

Introduction

The Composable Execution Layer (CEL) is the foundation of the Composable Frontend Architecture. It provides a consistent, context-aware way to author and execute business logic, behavioral orchestration, and state transitions across environments—whether it's client, server, edge, or embedded UI.

Historically, business logic and UI state were tightly coupled to frameworks or rendering contexts. This resulted in duplication (client/server variants), testing complexity, and limited reuse. CEL breaks this pattern by abstracting execution behavior into portable, declarative units that operate across platforms.

CEL allows teams to write logic once and use it anywhere—synchronously or asynchronously, inside or outside the UI framework. It enables testable, shareable logic that lives independently of rendering decisions.


Why We Need CEL

CEL provides a clean separation between logic and presentation, enabling teams to:

  • Write business logic once and reuse it across platforms.
  • Decouple UI layers from behavior, making refactors safer.
  • Optimize execution environments (e.g., run logic at the edge or server without rewriting).
  • Empower independent teams to build, test, and deploy autonomously.

Additional Reasons CEL is Critical Today

1. Avoiding Duplication Across Platforms

In multi-platform apps—web, mobile, desktop—developers often rewrite the same logic multiple times. CEL allows you to centralize business logic in one layer that feeds multiple frontends. This promotes consistency and drastically reduces bugs and rework. (leancode.co)

2. Enhancing Maintainability and Collaboration

When business rules are trapped inside UI components, they’re harder to find, maintain, or test. CEL enforces clear boundaries, which helps frontend and backend teams collaborate better without stepping on each other’s code. (medium.com)

3. Improving Testability

By isolating logic in pure functions, CEL makes it easy to write fast, reliable unit tests without setting up UI environments. This boosts confidence during refactors and CI/CD processes.

4. Enabling Scalable Architecture

CEL supports composable, scalable systems by allowing logic modules to be independently owned, deployed, and evolved. This is essential in large teams and micro-frontend architectures.

5. Ensuring Consistent User Experience

A shared CEL ensures business rules behave the same way across web, mobile, kiosk, and edge devices. This consistency improves both the user experience and developer reliability.

Modern applications face a complexity problem:

  • State is fragmented between components, stores, and backend APIs.
  • Logic is duplicated across client-side and server-side rendering paths.
  • Testing requires full framework scaffolding.

CEL addresses this by providing a dedicated execution layer:

  • Write once, run anywhere logic.
  • Unified treatment of user interaction, server-side data fetch, background sync, etc.
  • Support for multiple renderers (React, Web Components, SSR frameworks).

CEL creates a composable, reactive, testable logic tier that is:

  • Platform-agnostic
  • Observable and debuggable
  • Stateless or stateful (via injected contexts)

Architecture Overview

To understand how the Composable Execution Layer integrates within the broader architecture, it's helpful to visualize the separation of responsibilities between the UI, CEL, and underlying domain services.

At its core, CEL acts as the brain of the frontend system—decoupling business rules and logic flows from the rendering layer and ensuring that these rules can be invoked consistently regardless of where the application runs (browser, edge, or server).

Below is a simplified architectural diagram that illustrates how CEL connects to the UI and downstream domain services:

+-------------------------+
|    UI Renderer (React)  |
+------------+------------+
             ↓ calls
+-------------------------+
|  Composable Execution   |
|         Layer           |
+------------+------------+
             ↓ invokes
+-------------------------+
|  Domain Handlers / API  |
+-------------------------+

Explanation:

  • UI Renderer invokes actions, effects, or fetch logic via CEL APIs.
  • CEL encapsulates logic in composable units: tasks, effects, workflows.
  • Domain Handlers handle business operations, often in pure or async code.

Historical Context and Prior Art

To fully understand the value of the Composable Execution Layer, it helps to revisit how frontend logic has historically evolved—and where traditional paradigms have fallen short. The CEL was born out of necessity in a landscape where logic has grown increasingly fragmented and bound to presentation frameworks.

The Early Days: Coupling Everything

In the early days of web development, business logic was deeply entangled with the UI. PHP templates, jQuery functions, and even early React apps tied logic directly to buttons and components. This created brittle systems that were hard to reuse, test, or port.

Redux and Elm: Centralization with Limitations

The emergence of state containers like Redux and Elm provided a breath of fresh air. They emphasized centralized, pure logic and predictable state transitions. But the cost was boilerplate-heavy structures, poor async support, and tight coupling to component trees.

React Hooks: The Age of Scoped Behavior

React hooks introduced a new era of composability, giving developers the power to encapsulate behavior in small units. However, hooks are tightly bound to React’s lifecycle and cannot run independently of the UI. This made them difficult to reuse outside rendering contexts (e.g., SSR, background jobs).

CQRS & Command/Event Models

From the world of enterprise architecture, patterns like Command Query Responsibility Segregation (CQRS) and event-sourcing taught us how to model logic independently of UI. These ideas inspired CEL to treat tasks, effects, and side effects as orchestrated flows rather than imperative calls.

Actor Model: Logic as Message-Driven Modules

The Actor Model introduced the idea of independent, stateful units communicating via messages. CEL leverages this to build distributed workflows that respond to user actions or system events, making logic resilient, testable, and extensible.

Serverless & Edge Computing: Portability Becomes Critical

Finally, the explosion of edge computing and serverless architectures introduced a need for logic that can run anywhere. CEL is designed to execute in the browser, on the server, at the edge, or in a background worker—without needing to rewrite logic per context.


Implementation Examples

Below are implementation examples that show how CEL logic can be written once and reused across frameworks. In addition to usage, we also explore testing strategies and error handling.

TypeScript (Task Executor)

1 type TaskContext = { userId: string }
2
3 // A reusable CEL task to fetch user data
4 export async function fetchUser(ctx: TaskContext) {
5   const res = await fetch(`/api/user/${ctx.userId}`); // Platform-agnostic fetch call
6   return res.json(); // Return parsed JSON as the result
7 }

Explanation:

  • Line 1 defines a structured TaskContext to encapsulate input data—this encourages clarity and testability.
  • Line 4 is the entry point of a CEL task—an async function that can run on client, server, or edge.
  • Line 5 shows real-world side effect management (e.g., HTTP request), which is isolated from rendering.
  • Line 6 handles transformation of raw data into a usable object. CEL recommends placing parsing logic at this boundary.

React Usage

1 import { fetchUser } from './cel/fetchUser';
2
3 export function UserProfile({ userId }) {
4   const [user, setUser] = useState(null);
5
6   useEffect(() => {
7     fetchUser({ userId })
8       .then(setUser)
9       .catch(() => setUser({ name: 'Error loading user' }));
10   }, [userId]);
11
12   return <div>{user?.name}</div>;
13 }

Explanation:

  • Line 1 imports the reusable logic function created via CEL.
  • Line 4 initializes local state to hold the fetched user.
  • Line 6–10 performs the side effect using useEffect, demonstrating how CEL logic is dropped into React with no extra configuration.
  • Line 9 adds error handling—a crucial pattern for real-world resilience.
  • Line 12 renders the result, highlighting how rendering and logic are cleanly separated.

Explanation:

  • Line 1 imports the reusable logic function created via CEL.
  • Line 4 initializes local state to hold the fetched user.
  • Line 6–10 performs the side effect using useEffect, demonstrating how CEL logic is dropped into React with no extra configuration.
  • Line 9 adds error handling—a crucial pattern for real-world resilience.
  • Line 12 renders the result, highlighting how rendering and logic are cleanly separated.

Web Component Usage

1 class UserProfile extends HTMLElement {
2   connectedCallback() {
3     fetchUser({ userId: this.getAttribute('user-id') })
4       .then(user => {
5         this.innerHTML = `<b>${user.name}</b>`;
6       })
7       .catch(() => {
8         this.innerHTML = `<span>Error loading user</span>`;
9       });
10   }
11 }
12 customElements.define('user-profile', UserProfile);

Explanation:

  • Line 1 defines a native web component class.
  • Line 2 uses the lifecycle hook connectedCallback() to execute logic when the component is added to the DOM.
  • Line 3–9 shows logic reuse from CEL, complete with error handling for robustness.
  • Line 12 registers the element, enabling use like <user-profile user-id="123"></user-profile>.

Explanation:

  • Line 1 defines a native web component class.
  • Line 2 uses the lifecycle hook connectedCallback() to execute logic when the component is added to the DOM.
  • Line 3–9 shows logic reuse from CEL, complete with error handling for robustness.
  • Line 12 registers the element, enabling use like <user-profile user-id="123"></user-profile>.

Error Handling and Fault Tolerance

CEL logic is built to handle failures gracefully without locking up the UI. Always attach .catch() to promises or use try/catch in async functions to manage exceptions and show fallback UI or retry options.

async function safeFetchUser(ctx: TaskContext) {
  try {
    const user = await fetchUser(ctx);
    return { user };
  } catch (error) {
    console.error('Fetch failed', error);
    return { error: true };
  }
}

Testing CEL Tasks

Since CEL tasks are pure and decoupled from the renderer, they can be tested in isolation using standard test frameworks like Jest or Vitest.

import { fetchUser } from './cel/fetchUser';

it('fetches user by id', async () => {
  global.fetch = vi.fn().mockResolvedValue({
    json: () => Promise.resolve({ name: 'Alice' })
  });

  const result = await fetchUser({ userId: '123' });
  expect(result.name).toBe('Alice');
});

Testing Advantages:

  • No DOM or renderer setup required.
  • Clear input/output contract for every CEL unit.
  • Mocks and fakes are easy to inject via the task context.

Real-World Case Studies

🏢 Spotify – Shared Logic Across Surfaces

Background: Spotify operates across dozens of platforms—mobile (iOS/Android), desktop (Electron), web, smart TVs, and automotive systems. Maintaining consistent business logic across these platforms became a major challenge as teams duplicated authentication, user session handling, and playback behavior.

Problem:

  • Business logic was reimplemented independently on each platform, leading to inconsistencies and fragmented behavior.
  • Fixing bugs on one platform didn't ensure parity elsewhere, causing frustrating regression cycles.
  • UI framework dependencies made shared testing frameworks difficult to implement.

CEL Solution: Spotify introduced portable logic modules abstracted from rendering logic. These units could run independently in headless services, React Native, or embedded web players. By isolating behavior from UI code, teams built once and reused everywhere.

Results:

  • Uniform playback, auth, and user state behavior across platforms.
  • Streamlined debugging and QA across multi-device environments.
  • Easier A/B testing through decoupled feature toggles and CEL hooks.
  • Consistent user experiences.
  • Shared validation, playback, and authentication logic.
  • Easy A/B testing via feature-flagged CEL workflows.

Developer Insight:

“Moving workflows from React hooks to CEL helped us run logic in both SSR and background workers without rewriting it.” “Our validation logic used to live in three places—now we write it once and import it into any UI.”

🏢 Shopify – Server-Edge Hybrid Execution

Background: Shopify powers storefronts for millions of merchants. Their Hydrogen framework enables developers to build storefronts that can render and run logic at the server, edge, or client depending on performance, SEO, or personalization needs.

Problem:

  • Commerce logic (cart validation, pricing, promotions) needed to run consistently across environments.
  • Logic embedded in server-side code was hard to migrate or share with edge rendering.
  • Duplication of behavior across runtime boundaries increased the chance for bugs.

CEL-Inspired Architecture: Shopify adopted a CEL-style orchestration layer for commerce workflows. Each business process—like cart verification or shipping estimation—was composed into platform-independent tasks triggered by lifecycle hooks.

Results:

  • Code parity across client-rendered, edge-personalized, and server-rendered storefronts.
  • Stronger resilience in network-degraded environments (failover from client to edge).
  • Improved delivery velocity as logic could be tested and deployed once, reused everywhere.
  • Reduced logic drift across environments.
  • Instant failover between client and edge in case of network issues.
  • Better developer experience: same logic, tested once, reused everywhere.

Developer Insight:

“CEL gave us a single test surface for client/server workflows. Our mocks are smaller, and tests are faster.”

🏢 Airbnb – Composable Form Engine

Background: Airbnb’s platform relies heavily on forms—from listing creation to guest verification. These forms need to be dynamic, localized, and consistent across platforms while adhering to compliance and usability standards.

Problem:

  • Form logic—field visibility, validation, async workflows—was tightly coupled with UI components, making reuse difficult.
  • Teams building similar flows duplicated effort or introduced regressions.
  • Testing forms across platforms and markets was slow and brittle.

CEL-Driven Refactor: Airbnb developed a schema-first execution engine to separate form logic from UI. Each field referenced CEL tasks to drive behavior like conditional display, remote data fetching, or validation. This allowed for easier testing and previewing of forms without a UI.

Results:

  • Cross-platform consistency in form experience.
  • Localized behavior modules deployed independently of UI code.
  • Modular flows enabled new onboarding experiments and rapid internationalization.
  • UI framework-agnostic form logic (used in React, mobile web, and AMP).
  • Reusable validation chains across markets and languages.
  • Declarative testing of form workflows without a DOM.

Developer Insight:

“Our schema-driven form engine lets us launch new onboarding flows in days, not weeks. CEL helped us isolate logic and visualize flow logic in testing tools.” Airbnb collects large amounts of form data (host info, guest preferences, account setup, etc.). These forms must be fast, reliable, and easy to iterate on.

Problem:

  • Form logic was scattered across React components.
  • Validation and field visibility logic were duplicated across teams.
  • Updating form flows required manual regression testing.

CEL-Driven Refactor: Airbnb built a schema-first execution layer. Each form field described its behavior (visible if, validate when, load if) as CEL tasks, effects, and rules. Execution was decoupled from rendering.

Results:

  • UI framework-agnostic form logic (used in React, mobile web, and AMP).
  • Reusable validation chains across markets and languages.
  • Declarative testing of form workflows without a DOM.

Reusable Patterns and Strategic Benefits of CEL

CEL in Practice: Patterns and Anti-Patterns

✅ Recommended Patterns

  • Isolate logic into pure, testable functions: Pure functions without side effects are easier to test, debug, and reuse across environments.
  • Pass all context via typed objects (e.g., TaskContext): This keeps functions self-documenting and decoupled from global or implicit dependencies.
  • Co-locate related workflows and effects near the domain logic—not UI components: Improves modularity and reduces the coupling between views and business rules.
  • Reuse tasks across renderers (React, Web Components, SSR): Write once, deploy everywhere—ensures uniform behavior regardless of how and where the app renders.
  • Use CEL as the orchestration layer for background jobs, SSR preloads, or service workers: Avoids duplication by centralizing behavior, regardless of runtime context.

🚫 Anti-Patterns to Avoid

  • Calling DOM APIs or window.* inside CEL functions: Doing so breaks platform-agnostic design and makes the logic unusable in SSR, edge, or test environments.
  • Embedding presentation logic inside tasks: Tangles responsibilities, making it difficult to maintain or reuse behavior across projects.
  • Overloading a single task with too many responsibilities: Creates brittle, hard-to-test code. Favor small, composable tasks that do one thing well.
  • Mixing side effects and mutations without encapsulation: Use declarative flow or command chaining to manage complex execution cleanly.
  • Embedding presentation logic inside tasks (violates separation of concerns).
  • Overloading a single task with too many responsibilities—keep them composable.
  • Mixing side effects and mutations without encapsulation (prefer declarative chaining).

CEL Tooling and Developer Experience

To fully leverage CEL in complex applications, teams often benefit from enhanced observability and dev workflows:

  • Debuggable Observers: Instruments can trace CEL tasks, invocations, and emitted events.
  • Playground or Sandbox Runners: Run tasks outside the app context to prototype workflows.
  • Hot Module Reloading (HMR): Tasks can be hot-swapped in dev environments to speed iteration.
  • Visual Task Graphs: Use task registries to visualize dependencies and effects.
  • Test Framework Integrations: CEL logic fits naturally into Jest, Vitest, or Storybook Logic Test Kits.
  • ECN Compatibility: CEL integrates with the Event-Driven Component Network for real-time sync and distributed UI orchestration.

These tools help CEL act as both an engineering abstraction and a platform layer for teams building at scale.

CEL provides a clean separation between logic and presentation, enabling teams to:

  • Write business logic once and reuse it across platforms.
  • Decouple UI layers from behavior, making refactors safer.
  • Optimize execution environments (e.g., run logic at the edge or server without rewriting).
  • Empower independent teams to build, test, and deploy autonomously.
BenefitExpanded Explanation
Write OnceAuthor your business logic once and deploy it across multiple platforms—React, Web Components, or server.
Isolated & TestableLogic is separated from rendering concerns, making it easier to test in isolation with unit tests.
Platform-AgnosticCEL modules can run in browsers, on the server, or on edge runtimes without modification.
Reactive & ObservableYou can trace interactions through CEL tasks and observe emitted events, improving debugging and visibility.
OrchestratedCEL allows you to chain workflows, apply conditions (guards), and retry failures, making execution resilient and modular.

Additional Reasons CEL is Critical Today

1. Avoiding Duplication Across Platforms

In multi-platform apps—web, mobile, desktop—developers often rewrite the same logic multiple times. CEL allows you to centralize business logic in one layer that feeds multiple frontends. This promotes consistency and drastically reduces bugs and rework. (leancode.co)

2. Enhancing Maintainability and Collaboration

When business rules are trapped inside UI components, they’re harder to find, maintain, or test. CEL enforces clear boundaries, which helps frontend and backend teams collaborate better without stepping on each other’s code. (medium.com)

3. Improving Testability

By isolating logic in pure functions, CEL makes it easy to write fast, reliable unit tests without setting up UI environments. This boosts confidence during refactors and CI/CD processes.

4. Enabling Scalable Architecture

CEL supports composable, scalable systems by allowing logic modules to be independently owned, deployed, and evolved. This is essential in large teams and micro-frontend architectures.

5. Ensuring Consistent User Experience

A shared CEL ensures business rules behave the same way across web, mobile, kiosk, and edge devices. This consistency improves both the user experience and developer reliability.

Summary

The Composable Execution Layer is not just a technical solution—it's a strategic foundation that empowers modern frontend development. It enables code reuse, simplifies testing, and makes cross-platform consistency attainable. Whether you're building complex multi-surface apps or looking to improve maintainability in your current stack, CEL offers a path to better separation of concerns, faster iteration, and more scalable architectures.

Diagram: CEL in Action

[UI Event] ───► [CEL Task] ───► [Domain Logic]
                emits
              [CEL Event Bus]
               [APC / DIM / ECN]

CEL is the logic backbone of the composable frontend. It replaces ad hoc, framework-bound code with a unified, portable execution layer—scaling from local components to edge services.

Stay Tuned

Want to become a Next.js pro?
The best articles, links and news related to web development delivered once a week to your inbox.