# Saga

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.

## Classes

### Sagas

Defined in: [src/Saga.ts:144](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L144)

The triggers that [layer](https://effect-cqrs.dambox.ch/reference/api/saga/#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.

#### Extends

* `Shape`<`"effect-cqrs/Sagas"`, { `status`: `Effect`\<readonly [`Status`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#status)\[]>; `caughtUp`(`position?`): `Effect`<`void`, [`EventStoreError`](https://effect-cqrs.dambox.ch/reference/api/eventstore/#eventstoreerror)>; `parked`(`viewModel?`): `Effect`\<readonly `object`\[], [`EventStoreError`](https://effect-cqrs.dambox.ch/reference/api/eventstore/#eventstoreerror) | [`UnknownViewModel`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#unknownviewmodel)>; `replay`(`options`): `Effect`<[`ReplayResult`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#replayresult), [`UnknownViewModel`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#unknownviewmodel) | [`ViewModelError`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#viewmodelerror)>; `retryParked`(`options`): `Effect`<[`RetryParkedResult`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#retryparkedresult), [`EventStoreError`](https://effect-cqrs.dambox.ch/reference/api/eventstore/#eventstoreerror) | [`UnknownViewModel`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#unknownviewmodel) | [`ViewModelError`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#viewmodelerror)>; }, `this`>

#### Constructors

##### Constructor

> **new Sagas**(`_`): [`Sagas`](https://effect-cqrs.dambox.ch/reference/api/saga/#sagas)

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

###### Parameters

###### \_

`never`

###### Returns

[`Sagas`](https://effect-cqrs.dambox.ch/reference/api/saga/#sagas)

###### Inherited from

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

## Type Aliases

### SagaWorkflow

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

Defined in: [src/Saga.ts:46](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L46)

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

#### Type Parameters

##### Tag

`Tag` *extends* `string` = `string`

##### Fields

`Fields` *extends* `Schema.Struct.Fields` = `Schema.Struct.Fields`

##### Success

`Success` *extends* `Schema.Top` = `Schema.Top`

##### Error

`Error` *extends* `Schema.Top` = `Schema.Top`

## Variables

### CausationSchema

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

Defined in: [src/Saga.ts:40](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L40)

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.

***

### CommandOutcome

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

Defined in: [src/Saga.ts:160](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L160)

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

## Functions

### command()

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

Defined in: [src/Saga.ts:180](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L180)

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.

#### Type Parameters

##### Err

`Err` *extends* `Top` = `Never`

##### E

`E` = `never`

##### R

`R` = `never`

#### Parameters

##### name

`string`

##### options

###### error?

`Err`

###### execute

`Effect`<[`CommandResult`](https://effect-cqrs.dambox.ch/reference/api/aggregateservice/#commandresult)<`unknown`>, `E`, `R`>

#### Returns

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

***

### layer()

> **layer**<`Triggers`>(`sagas`): `Layer`<[`Sagas`](https://effect-cqrs.dambox.ch/reference/api/saga/#sagas), [`EventStoreError`](https://effect-cqrs.dambox.ch/reference/api/eventstore/#eventstoreerror), [`EventStore`](https://effect-cqrs.dambox.ch/reference/api/eventstore/#eventstore) | [`Checkpoints`](https://effect-cqrs.dambox.ch/reference/api/checkpoints/#checkpoints) | [`Context`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#context)<`Triggers`\[`number`]>>

Defined in: [src/Saga.ts:152](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L152)

Runs triggers in the background for as long as the layer lives, apart from the view models, and provides [Sagas](https://effect-cqrs.dambox.ch/reference/api/saga/#sagas) to manage them. Workflows usually run commands, so they sit on top of the command side, which view models are often provided to.

#### Type Parameters

##### Triggers

`Triggers` *extends* readonly [`Any`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#any)\[]

#### Parameters

##### sagas

`Triggers`

#### Returns

`Layer`<[`Sagas`](https://effect-cqrs.dambox.ch/reference/api/saga/#sagas), [`EventStoreError`](https://effect-cqrs.dambox.ch/reference/api/eventstore/#eventstoreerror), [`EventStore`](https://effect-cqrs.dambox.ch/reference/api/eventstore/#eventstore) | [`Checkpoints`](https://effect-cqrs.dambox.ch/reference/api/checkpoints/#checkpoints) | [`Context`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#context)<`Triggers`\[`number`]>>

***

### make()

> **make**<`Name`, `Aggregates`, `H`>(`options`): [`ViewModel`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#viewmodel-2)<`Name`, `HandlerError`<`H`>, `HandlerContext`<`H`>>

Defined in: [src/Saga.ts:127](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L127)

The trigger of a saga: a view model whose handlers start workflows with [start](https://effect-cqrs.dambox.ch/reference/api/saga/#start), so it keeps a checkpoint and retries like view models do. Run it with [layer](https://effect-cqrs.dambox.ch/reference/api/saga/#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.

#### Type Parameters

##### Name

`Name` *extends* `string`

##### Aggregates

`Aggregates` *extends* readonly [`Any`](https://effect-cqrs.dambox.ch/reference/api/aggregate/#any)\[]

##### H

`H` *extends* [`Handlers`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#handlers-1)<[`Event`](https://effect-cqrs.dambox.ch/reference/api/aggregate/#event-2)<`Aggregates`\[`number`]>>

#### Parameters

##### options

###### aggregates

`Aggregates`

###### handlers

`H`

###### name

`Name`

###### onFailure?

[`FailurePolicy`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#failurepolicy)

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

#### Returns

[`ViewModel`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#viewmodel-2)<`Name`, `HandlerError`<`H`>, `HandlerContext`<`H`>>

***

### start()

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

Defined in: [src/Saga.ts:104](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L104)

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.

#### Type Parameters

##### Tag

`Tag` *extends* `string`

##### Fields

`Fields` *extends* `Fields`

##### Success

`Success` *extends* `Top`

##### Error

`Error` *extends* `Top`

#### Parameters

##### self

[`SagaWorkflow`](https://effect-cqrs.dambox.ch/reference/api/saga/#sagaworkflow)<`Tag`, `Fields`, `Success`, `Error`>

##### payload

`MakeInView`<`Fields`>

#### Returns

`Effect`<`string`, `never`, `WorkflowEngine`>

***

### toLayer()

> **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](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L79)

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.

#### Type Parameters

##### Tag

`Tag` *extends* `string`

##### Fields

`Fields` *extends* `Fields`

##### Success

`Success` *extends* `Top`

##### Error

`Error` *extends* `Top`

##### R

`R`

#### Parameters

##### self

[`SagaWorkflow`](https://effect-cqrs.dambox.ch/reference/api/saga/#sagaworkflow)<`Tag`, `Fields`, `Success`, `Error`>

##### execute

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

#### Returns

`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()

> **workflow**<`Tag`, `Fields`, `Success`, `Error`>(`tag`, `options`): [`SagaWorkflow`](https://effect-cqrs.dambox.ch/reference/api/saga/#sagaworkflow)<`Tag`, `Fields`, `Success`, `Error`>

Defined in: [src/Saga.ts:57](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/Saga.ts#L57)

Defines the workflow of a saga. It is `Workflow.make` with a `causation` field added to the payload, which [start](https://effect-cqrs.dambox.ch/reference/api/saga/#start) fills in and [toLayer](https://effect-cqrs.dambox.ch/reference/api/saga/#tolayer) applies.

#### Type Parameters

##### Tag

`Tag` *extends* `string`

##### Fields

`Fields` *extends* `Fields`

##### Success

`Success` *extends* `Top` = `Void`

##### Error

`Error` *extends* `Top` = `Never`

#### Parameters

##### tag

`Tag`

##### options

###### error?

`Error`

###### idempotencyKey

(`payload`) => `string`

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

###### payload

`Fields`

###### success?

`Success`

###### suspendedRetrySchedule?

`Schedule`<`any`, `unknown`, `never`, `never`>

#### Returns

[`SagaWorkflow`](https://effect-cqrs.dambox.ch/reference/api/saga/#sagaworkflow)<`Tag`, `Fields`, `Success`, `Error`>
