Skip to content

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.