Contracts and Handlers
Durable authoring separates public contracts from executable implementations.
Schema values
↓
Workflow ──implement──> WorkflowHandler
Operation ──implement──> OperationHandlerWorkflow 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.
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:
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:
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.