Skip to content

Instance Management

EventFlow follows an event-sourced architecture where each machine instance (aggregate) maintains its state through a sequence of events. This page explains how instances are created, identified, and managed.

Event Sourcing Foundation

Every machine instance is an aggregate root in event sourcing terms:

Event Storeaggregate_idevent_typedataseqorder-123:checkout{customer}1order-123:payment_success{txn_id}2order-456:checkout{customer}1order-123:shipped{tracking}3

When an event arrives:

  1. Identify the target aggregate (by aggregate_id)
  2. Load all previous events for that aggregate
  3. Replay events to rebuild current state
  4. Process the new event
  5. Persist the new event to the store

Aggregate Lifecycle

Creating a New Aggregate

The first event in a machine's flow creates a new aggregate instance. The system automatically generates a unique aggregate ID:

flow
machine: @order

scenario: order lifecycle

  on> :checkout from @customer        // First event → creates NEW aggregate
    // aggregate_id auto-generated: "order-abc123"
    order moves to #pending
    emit :payment_request to @payment

When :checkout arrives:

  • No existing aggregate reference → System generates aggregate_id
  • New aggregate created with state #pending
  • Event stored: {aggregate_id: "order-abc123", event: ":checkout", ...}

INFO

The > prefix indicates this is an API/public endpoint (externally accessible). Whether it creates a new aggregate depends on whether it's the first event in the machine's flow, not on the > prefix itself.

Subsequent Events

Events after the first one target an existing aggregate and require a valid reference:

flow
on :payment_success from @payment   // Requires EXISTING aggregate
  order moves to #paid

on> :cancel_order from @customer    // API endpoint, but requires existing aggregate
  order moves to #cancelled

If a subsequent event arrives without a valid aggregate reference (via conversation context or explicit ID), the runtime throws an error.

Aggregate Reference

How does :payment_success know which order instance to update?

Answer: Through Conversation Context - see the Conversations page for details.

Event Routing Summary

The > prefix and aggregate creation are independent concepts:

  • > prefix = API/public endpoint (externally accessible)
  • Aggregate creation = determined by event position in flow (first event creates, subsequent events require reference)
Event TypeAPI Endpoint?Creates Aggregate?Routing
on> :event (first in flow)YesYes (new)New aggregate_id generated
on> :event (subsequent)YesNoRequires aggregate reference, else error
on :event (internal)NoDepends on contextConversation context → correct aggregate

Example

flow
machine: @order

scenario: checkout flow

  // First event → creates new aggregate (order-abc123)
  // Also an API endpoint (externally callable)
  on> :checkout from @customer
    order moves to #awaiting_payment
    emit :payment_request to @payment

  // Internal event → requires existing aggregate
  // Routed via conversation context
  on :payment_success from @payment
    order moves to #paid

  // API endpoint but NOT first event
  // Requires aggregate reference in payload, else runtime error
  on> :cancel_order from @customer
    ? order is in #awaiting_payment
      order moves to #cancelled

Runtime Error for Missing Reference

When a non-first event arrives without a valid aggregate reference:

EventFlowError: Cannot route event ':cancel_order' to @order
  - No aggregate_id provided in event payload
  - No conversation context available
  Hint: This event requires an existing order aggregate.
        Include 'order_id' in your request or use conversation context.

Instance Isolation

Each instance has its own:

  • State - current machine state
  • Context - all $variables
  • Event history - all events that have been processed
@order:abc123 → state: #paid, $total: 1200, $items: ["Laptop"]
@order:xyz789 → state: #pending, $total: 50, $items: ["Mouse"]

Instances never share state. They only communicate through events.

State Reconstruction

Because all events are stored, state can be reconstructed at any point:

Events for order-abc123:
  1. :checkout        → state becomes #pending
  2. :payment_request → (emit, no state change)
  3. :payment_success → state becomes #paid
  4. :shipped         → state becomes #shipped

Current state: #shipped

This enables:

  • Time-travel debugging - replay events to any point
  • Audit trails - complete history of all changes
  • State recovery - rebuild from events after failure

Multiple Instances

A machine definition is a template. Many instances can exist:

@order (machine definition)
  ├── @order:abc123 (instance 1)
  ├── @order:xyz789 (instance 2)
  ├── @order:def456 (instance 3)
  └── ... (any number of instances)

Each instance:

  • Processes its own events
  • Has its own state and context
  • Is identified by a unique aggregate_id

Released under the MIT License.