Sagas and Fan-out
The graph engine used loop nodes and gateway loops. The durable runtime uses Dart loops over recorded collections, forEach for durable fan-out, and saga for compensation.
forEach
dart
final results = await flow.forEach(
id: 'lines',
items: order.lines,
key: (line) => line.id,
body: (branch, line) => branch.serviceTask(reserveLine, line, id: 'reserve'),
);Item keys must be stable. JSON forEach evaluates itemsBinding and exposes $.item inside the body.
Saga
dart
final booked = await flow.saga(
id: 'booking',
steps: [
WorkflowSaga.step(
id: 'reserve',
execute: (ctx) => ctx.serviceTask(reserve, order, id: 'reserve'),
compensate: (ctx, reservation) =>
ctx.serviceTask(release, reservation, id: 'release'),
),
WorkflowSaga.step(
id: 'charge',
execute: (ctx) => ctx.serviceTask(charge, order, id: 'charge'),
compensate: (ctx, charge) =>
ctx.serviceTask(refund, charge, id: 'refund'),
),
],
);Compensation runs once, in reverse completion order. Each compensate is itself a durable service task with an idempotency key.