Skip to content

Sagas: reactions that span several aggregates and outlive one command, run as durable workflows from effect/workflow.

A saga has two halves. Its trigger is a view model that follows the log from a checkpoint and starts a workflow for each event it cares about. The workflow is the process itself: each command it runs is an Activity, so when the engine replays the workflow after a restart it skips the commands that already finished, and withCompensation undoes earlier steps when a later one fails.

Triggers see an event at least once, and the workflow’s idempotency key turns a second start into a join of the first execution. That only holds while the engine remembers the execution, so pair WorkflowEngine.layerMemory with in-memory checkpoints, as for view models, and a durable engine such as ClusterWorkflowEngine from effect/cluster with durable ones.

An activity that is cut off before its result is saved runs again, so the commands a saga runs should be idempotent: give each a commandId made from the execution id and the step, so that running it again returns the first result, or record the id of the transfer it belongs to.

Defined in: src/Saga.ts:144

The triggers that layer runs, managed like view models: their status, the events they parked and retrying those. It is ViewModel.ViewModels under another name, so it doesn’t stand in for the view models’ own.

new Sagas(_): Sagas

Defined in: node_modules/.pnpm/effect@4.0.6/node_modules/effect/dist/Context.d.ts:121

never

Sagas

Context.Service<Sagas, ViewModel.ViewModels["Service"]>()("effect-cqrs/Sagas").constructor

SagaWorkflow<Tag, Fields, Success, Error> = Workflow.Workflow<Tag, Schema.Struct<Fields & object>, Success, Error>

Defined in: src/Saga.ts:46

A workflow whose payload carries the causation of the event that started it.

Tag extends string = string

Fields extends Schema.Struct.Fields = Schema.Struct.Fields

Success extends Schema.Top = Schema.Top

Error extends Schema.Top = Schema.Top

const CausationSchema: Struct<{ causationId: String; correlationId: String; }>

Defined in: src/Saga.ts:40

The causation a workflow carries in its payload. A workflow body runs in the engine’s fibers, not in the trigger’s, so this is how commands it runs learn which event caused them.


const CommandOutcome: Struct<{ aggregateId: String; position: Int; version: Int; }>

Defined in: src/Saga.ts:160

What a saga keeps of a command it ran: enough to wait for view models to see it.

command<Err, E, R>(name, options): Activity<Struct<{ aggregateId: String; position: Int; version: Int; }>, Err, R>

Defined in: src/Saga.ts:180

Runs a command as an activity of a saga’s workflow, so a replay of the workflow returns the saved outcome instead of running it again.

Failures matching error, such as a domain error the saga reacts to, are saved like a success. A lost race or a store error is retried a few times. Anything else ends the workflow as a defect, which runs its compensations.

Err extends Top = Never

E = never

R = never

string

Err

Effect<CommandResult<unknown>, E, R>

Activity<Struct<{ aggregateId: String; position: Int; version: Int; }>, Err, R>


layer<Triggers>(sagas): Layer<Sagas, EventStoreError, EventStore | Checkpoints | Context<Triggers[number]>>

Defined in: src/Saga.ts:152

Runs triggers in the background for as long as the layer lives, apart from the view models, and provides Sagas to manage them. Workflows usually run commands, so they sit on top of the command side, which view models are often provided to.

Triggers extends readonly Any[]

Triggers

Layer<Sagas, EventStoreError, EventStore | Checkpoints | Context<Triggers[number]>>


make<Name, Aggregates, H>(options): ViewModel<Name, HandlerError<H>, HandlerContext<H>>

Defined in: src/Saga.ts:127

The trigger of a saga: a view model whose handlers start workflows with start, so it keeps a checkpoint and retries like view models do. Run it with layer, or with ViewModel.layer next to the other view models, where replaying it starts every saga again and joins the executions the engine still remembers.

Name extends string

Aggregates extends readonly Any[]

H extends Handlers<Event<Aggregates[number]>>

Aggregates

H

Name

FailurePolicy

What to do with an event that keeps failing, see ViewModel.park.

ViewModel<Name, HandlerError<H>, HandlerContext<H>>


start<Tag, Fields, Success, Error>(self, payload): Effect<string, never, WorkflowEngine>

Defined in: src/Saga.ts:104

Starts a saga’s workflow without waiting for it, and returns its execution id. Inside a trigger the event being handled becomes the saga’s cause; elsewhere the current causation does, or a new correlation when there is none.

Tag extends string

Fields extends Fields

Success extends Top

Error extends Top

SagaWorkflow<Tag, Fields, Success, Error>

MakeInView<Fields>

Effect<string, never, WorkflowEngine>


toLayer<Tag, Fields, Success, Error, R>(self, execute): Layer<never, never, WorkflowEngine | DecodingServices<Fields & object> | EncodingServices<Fields & object> | Success["DecodingServices"] | Success["EncodingServices"] | Error["DecodingServices"] | Error["EncodingServices"] | Exclude<R, Scope | WorkflowEngine | WorkflowInstance | Execution<Tag>>>

Defined in: src/Saga.ts:79

Registers the body of a saga’s workflow with the engine. Commands the body runs record the event that started the saga as their cause.

Tag extends string

Fields extends Fields

Success extends Top

Error extends Top

R

SagaWorkflow<Tag, Fields, Success, Error>

(payload, executionId) => Effect<Success["Type"], Error["Type"], R>

Layer<never, never, WorkflowEngine | DecodingServices<Fields & object> | EncodingServices<Fields & object> | Success["DecodingServices"] | Success["EncodingServices"] | Error["DecodingServices"] | Error["EncodingServices"] | Exclude<R, Scope | WorkflowEngine | WorkflowInstance | Execution<Tag>>>


workflow<Tag, Fields, Success, Error>(tag, options): SagaWorkflow<Tag, Fields, Success, Error>

Defined in: src/Saga.ts:57

Defines the workflow of a saga. It is Workflow.make with a causation field added to the payload, which start fills in and toLayer applies.

Tag extends string

Fields extends Fields

Success extends Top = Void

Error extends Top = Never

Tag

Error

(payload) => string

Identifies one run of the saga, such as a transfer id. Starting it again joins that run.

Fields

Success

Schedule<any, unknown, never, never>

SagaWorkflow<Tag, Fields, Success, Error>