You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The actor model handles fail-stop scenarios well (supervision, restart). It does NOT natively handle business-level multi-step workflows with compensating actions:
"Book hotel → book flight → book car. If car fails, cancel flight + hotel."
Today, users either roll their own state-machine inside a PersistentActor (verbose, error-prone) or pull in a separate workflow framework. Sagas / Process Managers are the canonical pattern:
Each step has a forward action + a compensating action.
On step failure, compensations run in reverse order.
State persisted at every step so failures recover.
This is L because it needs durable workflow state, compensation registry, retry/timeout policies, integration with PersistentActor. Not XL — well-bounded.
On failure of step N: steps 1..N-1 compensated in reverse order.
Design sketch — actor-ts equivalent
// src/saga/Saga.ts (new)exportinterfaceSagaStep<TInput,TOutput>{readonlyname: string;/** Forward action. Returns the result (passed to next step). */forward(input: TInput): Promise<TOutput>;/** Compensating action. Called with the FORWARD's output if forward succeeded. */compensate?: (forwardOutput: TOutput)=>Promise<void>;/** Retry policy for the forward action. Default: no retry. */retry?: {readonlymaxAttempts: number;readonlybackoffMs: number};/** Timeout for the forward action. Default: 30s. */timeoutMs?: number;}exportabstractclassSaga<TContext>extendsPersistentActor<SagaCmd,SagaEvent,SagaState<TContext>>{abstractsteps(): ReadonlyArray<SagaStep<unknown,unknown>>;abstractinitialContext(): TContext;// PersistentActor base handles:// - Persist (StepStarted, StepCompleted, StepFailed, CompensationStarted, CompensationCompleted) events// - Replay rebuilds saga state on recovery → resumes from last incomplete step// - Auto-run compensations on terminal failure// - Emit SagaCompleted / SagaFailed signal on terminal}
Compensation failure handling: what if a compensation itself fails? Retry policy on compensations? Recommend: retry per saga config; manual intervention on permanent failure (alert metric).
Step idempotency: forward and compensate must be idempotent (in case of retry/replay). Document; framework can't enforce.
Saga timeout (whole-saga): separate from step-timeout. Recommend: optional sagaTimeoutMs; if exceeded, switch to compensating regardless of current step.
External-call abstraction: steps interact with external services (HTTP, other actors). Sketch uses arbitrary async functions. Add explicit actorCall / httpCall helpers for common cases? Recommend: keep generic.
Test plan
Order-saga happy path — all 3 steps succeed; SagaCompleted emitted.
Size / Priority
Rationale
The actor model handles fail-stop scenarios well (supervision, restart). It does NOT natively handle business-level multi-step workflows with compensating actions:
Today, users either roll their own state-machine inside a
PersistentActor(verbose, error-prone) or pull in a separate workflow framework. Sagas / Process Managers are the canonical pattern:This is L because it needs durable workflow state, compensation registry, retry/timeout policies, integration with PersistentActor. Not XL — well-bounded.
Reference: what Lagom does
On failure of step N: steps 1..N-1 compensated in reverse order.
Design sketch — actor-ts equivalent
State tracked:
Lifecycle:
steps[0].forward.StepCompleted; recurse withsteps[1].StepFailed; switch to compensating; runsteps[N-1..0].compensatein reverse.SagaFailed; emit signal.SagaCompleted; emit signal.Recovery: replay events; resume from
currentStepIndex(whether in forward or compensating phase).Integration with existing actor-ts subsystems
PersistentActor: base class; saga inherits.BackoffSupervisor: optional wrapping if saga itself crashes.Inbox([Feature] Inbox — actor-level dedup for non-actor callers #181): external triggers can be dedup'd.Out of scope / non-goals
Open design questions
sagaTimeoutMs; if exceeded, switch to compensating regardless of current step.actorCall/httpCallhelpers for common cases? Recommend: keep generic.Test plan
Acceptance criteria
Saga<TContext>base class extendingPersistentActor.SagaStep<TInput, TOutput>interface.