Skip to content

Cluster.Workflow reference

Source: packages/effect-cluster/src/Workflow.ts

A durable Workflow on an Effect Cluster: each run is a workflow entity, so its activities are persisted, DurableClock.sleep is a delayed message, and a run continues on another runner if its runner dies.

Declare it like Effect’s Workflow.make, from a name and its schemas, with its implementation: an Effect that resolves bindings once per runner and returns the handler. Providing a pool Layer to the implementation is what deploys it. Or declare it bare and implement it with .make(props, impl), as a Layer: callers import the bare declaration without bundling the implementation or its pool.

yield* PlaceOrder from any runtime returns its client: start a run, then read, signal or interrupt it.

src/PlaceOrder.ts
export default class PlaceOrder extends Cluster.Workflow<PlaceOrder>()(
"PlaceOrder",
{
payload: { orderId: Schema.String },
success: Schema.String,
idempotencyKey: ({ orderId }) => orderId,
},
{ main: import.meta.url },
Effect.gen(function* () {
const counters = yield* Counter;
return Effect.fn(function* ({ orderId }) {
const n = yield* Activity.make({
name: "count",
success: Schema.Int,
execute: Effect.flatMap(counters.getByName("orders"), (counter) =>
counter.Increment({ amount: 1 }),
),
});
return `order ${orderId} is #${n}`;
});
}).pipe(Effect.provide(Default)),
) {}

Workflow: Keeping its code out of its callers

Section titled “Workflow: Keeping its code out of its callers”

A bare declaration, which callers import

src/PlaceOrder.ts
export class PlaceOrder extends Cluster.Workflow<PlaceOrder>()("PlaceOrder", {
payload: { orderId: Schema.String },
success: Schema.String,
idempotencyKey: ({ orderId }) => orderId,
}) {}

Its implementation, as a Layer, deployed to a pool

src/PlaceOrderLive.ts
export default PlaceOrder.make(
{ main: import.meta.url },
Effect.gen(function* () {
const counters = yield* Counter;
return PlaceOrder.of(
Effect.fn(function* ({ orderId }) {
const n = yield* Activity.make({
name: "count",
success: Schema.Int,
execute: Effect.flatMap(counters.getByName("orders"), (counter) =>
counter.Increment({ amount: 1 }),
),
});
return `order ${orderId} is #${n}`;
}),
);
}).pipe(Effect.provide(Default)),
);

Start a run from any runtime

const placeOrder = yield* PlaceOrder;
const run = yield* placeOrder.start({ orderId: "o-1" });
const status = yield* run.status;

A child workflow, inside another workflow

const receipt = yield* PlaceOrder.execute({ orderId: "o-1" });