Test without a database
Assert what your code would do.
Pipelines return inert objects. Walk the tree and check each step: no mock, no in-memory fake, no container.
assert.equal(step.cmd.name, 'cmdFindUser'); assert.equal(step.type, 'Command');
Replay production runs
Step through the failed request locally.
Record what each command returned in production, then feed those results back into the same tree to retrace the exact path, no infrastructure attached.
let step = flow; while (step.type === 'Command') step = step.next(recorded[step.cmd.name]); // replays the recorded path, no I/O
Retry as data
Test resilience without waiting.
Wrap any Effect with retry semantics. Because the config is plain data, tests assert on it directly: no timers, no sleeps, no flaky timing.
Retry(effect, { attempts: 3, delay: 200, backoff: 2 }); // 200·400·800
Parallel execution
Concurrent branches, ordered results.
Promise.all semantics with first-failure short-circuiting. Ask context flows into every branch automatically.
Parallel( [getUser(id), getPerms(id)], ([user, perms]) => Success({ user, perms }) );
Dependency injection
Context without polluting signatures.
Resolve tenant, trace id, or config from the framework layer. Domain functions stay clean and just Ask.
Ask((ctx) => { // ctx.tenant comes from runEffect return Command(...) });
OpenTelemetry
Lifecycle hooks for tracing.
onRun, onStep, and onBeforeCommand let you wrap workflows in spans without touching domain code.
configureEffect({ onRun: (eff, pipeline, name) => tracer.startActiveSpan(name, pipeline) });