Implementing Operations
Operations are the only workflow-authored place for external effects.
Declare First
dart
final publish = Operation<
Document,
PublicationReceipt,
PublicationFailure
>(
name: 'documents.publish',
version: 1,
input: documentSchema,
output: receiptSchema,
failure: publicationFailureSchema,
);Bind the Handler
dart
final publishHandler = publish.implement((operation, document) async {
await operation.context.heartbeat();
return publisher.publish(
document,
idempotencyKey: operation.context.idempotencyKey,
);
});Idempotency
Retries may execute the handler more than once. Use the provided key to:
- claim a unique effect receipt;
- send an idempotency header to an external API;
- guard an upsert or database function;
- return the previously recorded result after a duplicate attempt.
Expected Versus Unexpected Failure
Use operation.fail(domainFailure, retryable: false) for a modeled business outcome. Throw OperationHandlerException for operational classification when there is no typed domain failure. Unexpected exceptions become defects.
Capabilities
Process-local dependencies can be provided through a WorkflowLayer and resolved with operation.require(capability). Capabilities never enter workflow replay state.
Heartbeats and Leases
Long-running handlers should heartbeat before their lease expires. A stale worker cannot complete after another worker claims a newer generation.