Skip to content

Start & End Nodes

Start and End nodes define the entry and terminal points of a workflow.

Start Node

Every workflow must have exactly one start node.

Definition

dart
builder.start('begin');

// With custom name
builder.start('begin', name: 'Start Order Processing');

Behavior

When a workflow starts:

  1. Engine creates a new WorkflowInstance
  2. Creates a token at the start node
  3. Start executor finds the outgoing edge
  4. Returns ContinueResult to move to the next node

Executor

dart
class StartNodeExecutor extends NodeExecutor {
  @override
  Future<NodeResult> execute(WorkflowContext context) async {
    final outgoingEdge = context.outgoingEdges.first;
    return ContinueResult.single(outgoingEdge.targetNodeId);
  }
}

End Node

Workflows can have multiple end nodes for different outcomes.

Definition

dart
// Basic end node
builder.end('completed');

// Named end nodes for different outcomes
builder.end('approved', name: 'Request Approved');
builder.end('rejected', name: 'Request Rejected');
builder.end('cancelled', name: 'Request Cancelled');

Behavior

When a token reaches an end node:

  1. End executor returns CompleteWorkflowResult
  2. Token is deactivated
  3. If all tokens are inactive, workflow status becomes completed

Executor

dart
class EndNodeExecutor extends NodeExecutor {
  @override
  Future<NodeResult> execute(WorkflowContext context) async {
    return CompleteWorkflowResult(
      output: context.currentNode.config['output'] ?? {},
    );
  }
}

Multiple End Nodes Pattern

Use multiple end nodes to represent different workflow outcomes:

dart
final workflow = WorkflowBuilder(...)
    .start('begin')
    .task('validate', ...)
    .oneOf('checkValid', [
      Branch.whenFn((o) => o['isValid'] == true, then: 'process'),
      Branch.otherwise(then: 'invalid'),
    ])
    .task('process', ...)
    .end('success', name: 'Successfully Processed')
    .end('invalid', name: 'Validation Failed')
    .end('error', name: 'Processing Error')
    .connect('begin', 'validate')
    .connect('validate', 'checkValid')
    .connect('process', 'success')
    .build();

Flow Diagram

Best Practices

1. Name end nodes descriptively

dart
// GOOD - Clear outcome names
builder.end('approved', name: 'Request Approved');
builder.end('rejected', name: 'Request Rejected');

// AVOID - Generic names
builder.end('end1');
builder.end('end2');

2. Use a limited number of end nodes

A workflow should have 2-4 end nodes representing distinct outcomes:

  • Success path
  • Failure/error path
  • Cancelled/abandoned path
  • Optional: timeout path

3. Ensure all paths lead to end nodes

Every possible execution path should terminate at an end node. Use the default branch in gateways to catch unexpected cases.

Next Steps