Skip to content

Contracts and Handlers ​

Durable authoring separates public contracts from executable implementations.

text
Schema values
        ↓
Workflow ──implement──> WorkflowHandler
Operation ──implement──> OperationHandler

Workflow Contract ​

Workflow<I, O, E> owns immutable code + version identity plus input, successful-output, and expected-failure schemas. It does not own the orchestration function, fingerprint, runtime state, or storage.

dart
final order = Workflow<Order, Receipt, OrderFailure>(
  code: 'orders.process',
  version: 1,
  input: orderSchema,
  output: receiptSchema,
  failure: orderFailureSchema,
);

final orderHandler = order.implement(
  fingerprint: deploymentFingerprint,
  execute: (workflow, input) async {
    // Deterministic orchestration only.
  },
);

Changing handler behavior requires a new workflow version and fingerprint.

Operation Contract ​

Operation<I, O, E> owns immutable name + version identity and the same three data boundaries. Operation.implement(...) creates its real worker implementation:

dart
final reserveHandler = reserve.implement((operation, order) {
  return gateway.reserve(
    order,
    idempotencyKey: operation.context.idempotencyKey,
  );
});

Operation handlers may perform effects. Workflow handlers may not. A workflow runs the contract directly:

dart
final reservation = await workflow.perform(
  reserve,
  order,
  id: 'reserve',
  schedule: WorkflowSchedule.spaced(
    const Duration(seconds: 2),
    attempts: 3,
  ),
  timeout: const Duration(seconds: 30),
  idempotencyKey: 'reserve:${order.id}',
);

Why Schema Is Separate ​

The same data contract can describe an entity representation, command, event, configuration, stored record, workflow input, operation output, or expected failure. Schema therefore has no workflow prefix and no execution behavior.