Skip to content

Core Philosophy

The Vyuh Workflow Engine separates workflow shape, executable behavior, runtime state, and storage.

Token-Based Execution

Workflow instances move tokens through a graph. Tokens make parallel execution and recovery explicit:

Runtime state persists the active/waiting/consumed token positions. After a crash, the engine can find stale running instances and resume active tokens.

Definition vs Execution

The current model has two important shapes:

ShapePurpose
WorkflowDefinitionPersisted definition data. Used by storage, templates, JSON, and the editor library.
WorkflowExecutable graph loaded by the engine. Node configs and processors are resolved before execution.

This keeps storage simple and lets the engine choose the right resolver for production, simulation, or tests.

Descriptor-Based Logic

Workflow definitions carry schema types. Business logic lives in descriptors:

dart
final descriptor = WorkflowDescriptor(
  title: 'Email tasks',
  tasks: [SendEmailTaskExecutor.typeDescriptor],
);

final context = RegistryTypeResolver(
  descriptors: [
    DefaultWorkflowDescriptor(),
    descriptor,
  ],
);

final engine = WorkflowEngine(
  context: context,
  storage: InMemoryStorage(),
  executionMode: ExecutionMode.production,
);
await engine.initialize();

Benefits:

  • Definitions stay serializable.
  • Executors are reusable and unit-testable.
  • Runtime can load definitions from storage without embedding executable code in JSON.
  • Simulation can substitute passthrough executors without changing definitions.

Effect-Based Outputs

Executors do not mutate workflow state directly. They return results and effects:

dart
return TaskSuccess([
  SetOutputEffect(output: {'sent': true}, path: 'notification'),
  RouteToPortEffect(portId: 'success'),
]);

The engine processes effects centrally, which keeps persistence, event emission, token updates, user-task creation, and cancellation in one place.

Signal Coordination

External interactions resume workflows through signals:

dart
await engine.sendSignal(
  workflowInstanceId: instance.id,
  node: 'approval_decision',
  port: 'approve',
  payload: {'output': decisionPayload},
);

Signals can target node ID, schema type, or signal name. Ports can target output port IDs or labels.

Storage-Agnostic Runtime

The engine only depends on WorkflowStorage. CDX ships:

  • InMemoryStorage for tests, demos, and editor simulation.
  • SupabaseStorage for Supabase/PostgreSQL deployments.

Custom adapters can implement the same repository interfaces.

Templates Over Repeated Graphs

For CDX approvals, use ApprovalTemplate rather than rebuilding similar approval graphs in each app. The canonical approval workflow handles:

  • Multi-level chain execution.
  • Approval, rejection, revision, resubmission, cancellation, and failure outcomes.
  • Domain dispatch through item_schema_type.
  • Typed server actions for Flutter inbox rendering.

Editor Is A Simulation Surface

vyuh_workflow_editor can load workflows from storage and descriptors, but editor execution runs in simulation mode so authors can manually drive decision points, timers, subflows, and multi-output routes.

See Also