Vyuh Workflows
Write long-running processes as typed Dart. Operations, requested work, events, timers, and child flows are durable boundaries that resume after crashes, restarts, or worker changes.
Why Durable Workflows?
Deterministic Replay
Workflows are Dart functions. After a restart, the runtime replays recorded history and stops at the next unresolved durable await.
Assigned Work
Work items wait in storage, not in a process. Any eligible actor can claim and respond to requested work.
Transactional History
Runs, events, and commands commit together. PostgreSQL leases, fencing, and SKIP LOCKED keep competing workers honest.
One Vocabulary, Two Surfaces
Author in typed Dart or constrained JSON. Both produce a WorkflowHandler that executes in WorkflowRuntime.
Getting Started
Core Concepts
- Vocabulary — Schemas, definitions, handlers, causes, and canonical verbs
- Definitions and Handlers — Separate immutable public boundaries from executable implementations
- Failures and Causes — Preserve expected failure, defect, cancellation, timeout, and composite causes
- Workflow — A versioned Dart or JSON definition with stable operation IDs
- Runs and History — An execution is a run plus immutable events and commands
- Registration — Install workflows and handlers through WorkflowModule
- Replay — Completed awaits return recorded results; unresolved ones block
- Bindings — How JSON workflows read input and published step results
- CDX Integration — Templates, Blueprint, inbox UI, and the workflow service
Durable Vocabulary
| Await | Purpose |
|---|---|
| Operation | Idempotent automated work |
| Work | Assigned work for any actor |
| Event | Typed communication between execution paths |
| Timer | Durable sleep |
| Child workflow | Call, spawn, and join |
Patterns & Examples
Quick Example
import 'package:vyuh_workflow_engine/vyuh_workflow_runtime.dart';
final reserve = Operation<Order, Reservation, OrderFailure>(
name: 'orders.reserve',
version: 1,
input: orderSchema,
output: reservationSchema,
failure: orderFailureSchema,
);
final approve = Work<Reservation, Approval>(
name: 'orders.approve',
input: reservationCodec,
response: approvalCodec,
);
final orderApprovalDefinition = Workflow<
Order,
Result,
OrderFailure
>(
code: 'orders.approval',
version: 1,
input: orderSchema,
output: resultSchema,
failure: orderFailureSchema,
);
final orderApprovalHandler = orderApprovalDefinition.implement(
fingerprint: buildFingerprint,
execute: (workflow, order) async {
final reservation = await workflow.perform(
reserve,
order,
id: 'reserve',
schedule: WorkflowSchedule.exponential(
const Duration(seconds: 1),
attempts: 3,
),
);
final decision = await workflow.request(
approve,
reservation,
id: 'approve',
title: 'Approve order',
audience: const Audience(
roleIds: ['order-approver'],
),
);
return decision.approved ? Result.approved() : Result.rejected();
},
);
final runtime = WorkflowRuntime(
storage: InMemoryWorkflowStorage(),
modules: [
WorkflowModule(
name: 'orders',
workflows: [orderApprovalHandler],
operations: [
reserve.implement(
(operation, order) => reservationService.reserve(
order,
idempotencyKey: operation.context.idempotencyKey,
),
),
],
work: [approve],
),
],
);
final run = await runtime.start(orderApprovalHandler, order);New application code imports vyuh_workflow_runtime.dart. The graph subsystem is explicit through Legacy* names and is separate from durable authoring.
For CDX approvals, use ApprovalWorkflowHandlerFactory from cdx_workflow_templates and respond to work through the workflow service. See Approval Workflows and CDX Integration.