Skip to content

Quick Start

Build a complete approval workflow in 5 minutes.

The Scenario

We'll create a workflow that:

  1. Validates an incoming request
  2. Waits for approval via an external signal
  3. Routes to different outcomes based on the decision

Complete Example

dart
import 'package:vyuh_workflow_engine/vyuh_workflow_engine.dart';

Future<void> main() async {
  // 1. Create deserialization context
  final context = RegistryDeserializationContext(
    descriptors: [DefaultWorkflowDescriptor()],
  );

  // 2. Create storage (in-memory for this example)
  final storage = InMemoryStorage(context: context);

  // 3. Initialize the engine
  final engine = WorkflowEngine(
    context: context,
    storage: storage,
  );
  await engine.initialize();

  // 3. Define the approval workflow
  final workflow = WorkflowBuilder('APPROVAL', 'Simple Approval Workflow')
      // Entry point
      .start('begin')

      // Validate the request
      .task('validate', name: 'Validate Request', execute: (ctx) async {
        // Use getInitial<T> for original workflow input
        final entityId = ctx.getInitial<String>('entityId');
        if (entityId == null) {
          throw ArgumentError('entityId is required');
        }
        return {'validated': true, 'timestamp': DateTime.now().toIso8601String()};
      })

      // Wait for approval decision
      .signalWait(
        'awaitApproval',
        name: 'Await Approval',
        signal: 'approval_decision',
        storeAs: 'approvalResult',
      )

      // Route based on decision
      .oneOf('routeDecision', [
        Branch.whenFn(
          (output) => output['approvalResult']?['decision'] == 'approved',
          then: 'handleApproved',
          label: 'Approved',
        ),
        Branch.whenFn(
          (output) => output['approvalResult']?['decision'] == 'rejected',
          then: 'handleRejected',
          label: 'Rejected',
        ),
        Branch.otherwise(then: 'handleRejected'),
      ])

      // Handle approved
      .task('handleApproved', name: 'Process Approval', execute: (ctx) async {
        // Use getAny<T> with dot-notation for accumulated output
        final approvedBy = ctx.getAny<String>('approvalResult.approvedBy');
        print('Request approved by $approvedBy');
        return {'finalStatus': 'approved', 'processedAt': DateTime.now().toIso8601String()};
      })

      // Handle rejected
      .task('handleRejected', name: 'Process Rejection', execute: (ctx) async {
        print('Request rejected');
        return {'finalStatus': 'rejected'};
      })

      // Terminal nodes
      .end('completed', name: 'Completed')
      .end('rejected', name: 'Rejected')

      // Define the flow
      .connect('begin', 'validate')
      .connect('validate', 'awaitApproval')
      .connect('awaitApproval', 'routeDecision')
      .connect('handleApproved', 'completed')
      .connect('handleRejected', 'rejected')
      .build();

  // 4. Register the workflow
  engine.registerWorkflow(workflow);

  // 5. Start a workflow instance
  final instance = await engine.startWorkflow(
    workflowCode: workflow.code,  // Use workflowCode for semantic lookup
    input: {
      'entityId': 'REQ-001',
      'entityType': 'PurchaseOrder',
      'submittedBy': '[email protected]',
    },
  );

  print('Workflow started: ${instance.id}');
  print('Status: ${instance.status}'); // waitingForSignal

  // 6. Simulate approval (in real app, this comes from UI)
  await engine.sendSignal(
    workflowInstanceId: instance.id,
    node: 'awaitApproval',  // The node ID waiting for the signal
    payload: {
      'decision': 'approved',
      'approvedBy': '[email protected]',
      'comments': 'Looks good!',
    },
  );

  // 7. Check final status
  final completed = await storage.instances.getById(instance.id);
  print('Final status: ${completed?.status}'); // completed
  print('Output: ${completed?.output}');
}

Understanding the Flow

Key Concepts Introduced

ConceptDescription
WorkflowBuilderFluent API for defining workflows
start()Entry point for the workflow
task()Automated work execution
signalWait()Pause and wait for external signal
oneOf()Exclusive gateway (XOR) for routing
BranchDefine routing conditions
end()Terminal point for the workflow
connect()Connect nodes in the flow

Next Steps