DEV Community

Cover image for Pipeline Anatomy — What Happens When You Update State in SDuX Vault
SDuX Vault
SDuX Vault

Posted on

Pipeline Anatomy — What Happens When You Update State in SDuX Vault

What actually happens when you call replaceState or mergeState in SDuX Vault? The answer is not "the value gets written to a store." The answer is a deterministic, multi-stage pipeline that resolves, filters, reduces, and commits your state change — atomically, every time.

This post walks through that journey step by step.

A State Update's Journey

When you update state in SDuX Vault, your value enters a structured pipeline. The pipeline is not a metaphor — it's a literal, ordered sequence of stages that every state update passes through before it becomes visible to your application.

Here's the path, in order:

  1. Policy (Controllers) — Can this update proceed at all?
  2. Interceptors — Should this update be admitted?
  3. Resolve — Normalize the input into a resolved candidate value.
  4. Merge — Combine the candidate with the current committed state.
  5. Operators — Refine or suppress the merged candidate.
  6. Filters — Further refine or reject the candidate before reduction.
  7. Reducers — Compute the finalized state from the processed value.
  8. Taps — Observe the result (before and after reduction).
  9. Persist / Encrypt — Store the committed snapshot externally.
  10. State Commitment — Emit the final, immutable snapshot.

Every stage is opt-in. A minimal FeatureCell uses only the core stages (resolve, merge, state). When you add a filter behavior, it runs at the filter stage. When you add a persistence behavior, it runs at the persist stage. Nothing runs unless you declare it.

Key takeaway: The pipeline is not a black box. Every stage has a fixed position, a clear responsibility, and explicit opt-in registration.

Behaviors — The Data Path

Behaviors are the composable building blocks that occupy pipeline stages. Each Behavior performs exactly one function: resolving input, filtering values, reducing state, observing snapshots, or persisting output.

Here's what makes Behaviors different from middleware or effects in other state systems:

  • Stage-bound. A Behavior runs at exactly one pipeline stage. A filter Behavior cannot run during reduction. A persist Behavior cannot intercept input. The pipeline enforces this structurally.
  • Explicitly registered. A Behavior participates only if you include it in the FeatureCell declaration. If it's not listed, it doesn't execute. There is no implicit behavior discovery.
  • Composable without coupling. Behaviors don't invoke or coordinate with each other. Each one operates on the inputs provided to its stage and returns a result. The pipeline handles ordering and execution.

There are two categories:

Core Behaviors — Always present. These handle scheduling, input normalization, default merge, and error finalization. You don't register them; they're guaranteed for every FeatureCell.

Addon Behaviors — Optional, engineer-selected. These provide capabilities like filtering, operators, taps, persistence, encryption, and error shaping. You register them explicitly when configuring a FeatureCell.

export const employeeCell = FeatureCell(
  EmployeeCell,
  {
    key: 'employees',
    initialState: []
  },
  [
    // Addon behaviors — each occupies a specific pipeline stage
    withArrayMergeBehavior,
    withDistinctUntilChangedOperator,
    withLocalStoragePersistBehavior
  ]
);
Enter fullscreen mode Exit fullscreen mode

In this declaration, three addon Behaviors extend the pipeline:

  • withArrayMergeBehavior runs at the merge stage, replacing the default merge strategy with array-aware merging.
  • withDistinctUntilChangedOperator runs at the operators stage, suppressing duplicate emissions.
  • withLocalStoragePersistBehavior runs at the persist stage, writing committed snapshots to localStorage.

Each one has a fixed position in the pipeline. You don't think about ordering — SDuX Vault validates and inserts each Behavior into the correct stage automatically.

Controllers — The Policy Path

While Behaviors handle the data path (what happens to the value), Controllers handle the policy path (whether execution proceeds at all).

Controllers are coordinating authorities. They don't transform state. They don't produce values. Instead, they mediate, arbitrate, and finalize control decisions that govern how the pipeline executes.

Think of Controllers as policy enforcement for your state updates:

  • A throttle controller limits how frequently updates can run.
  • A max-failures controller halts the pipeline after repeated errors.
  • A tab-sync controller coordinates state updates across browser tabs.
  • A stepwise controller manages multi-phase resolution.

Controllers operate across pipeline stages rather than within a single stage. They observe requests emitted by Behaviors, apply centralized decision logic, and issue authoritative outcomes — proceed, deny, buffer, or retry.

export const employeeCell = FeatureCell(
  EmployeeCell,
  {
    key: 'employees',
    initialState: []
  },
  [
    withStepwiseResolveBehavior // Behavior (data path)
  ],
  [
    withStepwiseController // Controller (policy path)
  ]
);
Enter fullscreen mode Exit fullscreen mode

The fourth array in a FeatureCell declaration is the controller array. Like Behaviors, Controllers are explicitly registered. If a Behavior requires a Controller for coordination, both must be declared — SDuX Vault fails fast if a dependency is missing, rather than silently executing with incomplete authority.

Key takeaway: Behaviors define what happens to your data. Controllers define whether and when it happens. This separation keeps control logic centralized and deterministic instead of scattered across middleware.

The Execution Guarantee

This is where SDuX Vault fundamentally differs from other state management systems. The pipeline doesn't just process your update — it guarantees how that processing occurs.

Compute First, Commit Later

SDuX Vault executes state updates in two distinct phases:

  1. Pipeline computation — Determines what the next state should be. All interceptor evaluation, resolve execution, operator/filter/reducer processing, and error normalization happens here. No state is mutated during this phase.

  2. State commitment — Makes the result visible. Signal updates, state callbacks, DevTools notifications, and controller notifications all occur inside a queueMicrotask after computation completes.

These phases are intentionally separated and never interleaved. No mutation, signal emission, or observer notification occurs until pipeline computation has completed successfully. Partial results and intermediate values are never observable outside the pipeline.

Atomic Snapshots

Every successful pipeline execution produces exactly one atomic snapshot. That snapshot is:

  • Fully resolved
  • Fully filtered
  • Fully reduced
  • Fully normalized
  • Fully encrypted and persisted (if applicable)

Observers never see intermediate states, partially reduced values, or pre-normalized data. Either the entire snapshot is committed, or no state change is visible at all.

Reentrancy Is Structurally Impossible

Because state commitment is deferred to a microtask, any attempt to trigger a new state update from within a reducer, state callback, or error handler will always occur after the current commit has completed.

This eliminates entire classes of bugs:

  • Dispatching during reducer execution
  • Promise resolution interleaving with state writes
  • Observer-triggered infinite loops

These aren't prevented by convention or lint rules. They're prevented by architecture.

Why This Matters: Before vs. After

Before (Traditional State Management)

In a typical Redux-style system, a state update triggers middleware, reducers, and effects — often in unpredictable order. Side effects fire during reduction. Observers see partial state. Race conditions emerge when async actions resolve out of order.

Debugging means tracing through action dispatches, middleware chains, effect handlers, and selector memoization — with no guarantee that what you see in DevTools represents what actually happened.

After (SDuX Vault Pipeline)

A state update enters the pipeline. It passes through declared stages in deterministic order. Every Behavior has a fixed position. Controllers enforce policy before data processing begins. Computation finishes completely before any observer is notified.

Debugging means looking at one pipeline execution: input in, snapshot out, every stage visible in DevTools with timing and category markers.

The difference isn't incremental. When your state system guarantees atomic, deterministic, reentrant-safe execution by construction, entire categories of bugs simply cannot exist.

Try It Yourself

Open a live StackBlitz demo to see the pipeline in action — trigger state updates and watch each stage execute in real time:

Open a StackBlitz demo →

For the full pipeline stage reference, see the Pipeline Architecture documentation. To understand how Behaviors and Controllers compose, start with What Is a Behavior? and What Is a Controller?.

Top comments (0)