Instance Management
Machine vs Instance
In EventFlow, it's essential to understand the distinction between a machine and an instance:
| Concept | What It Is | Example |
|---|---|---|
| Machine | A template - the behavior definition in your .flow file | machine: @order |
| Instance | A running entity with its own state and data (also called aggregate) | @order:abc123 |
When you write machine: @order, you're defining a template. At runtime, many instances can exist:
@order (machine/template)
├── @order:abc123 (instance) → state: #processing
├── @order:xyz789 (instance) → state: #paid
└── @order:def456 (instance) → state: #shippedKey Insight
You define behavior once in your .flow file. The runtime creates and manages potentially thousands of independent instances, each with its own state and context.
Event-Sourced Architecture
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:
When an event arrives:
- Identify the target aggregate (by aggregate_id)
- Load all previous events for that aggregate
- Replay events to rebuild current state
- Process the new event
- 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:
machine: @order
scenario: order lifecycle
on :checkout from @customer (api) // First event → creates NEW aggregate
// aggregate_id auto-generated: "order-abc123"
order moves to #pending
emit :payment_request to @paymentWhen :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 (api) suffix 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 (api) suffix itself.
Subsequent Events
Events after the first one target an existing aggregate and require a valid reference:
on :payment_success from @payment // Requires EXISTING aggregate
order moves to #paid
on :cancel_order from @customer (api) // API endpoint, but requires existing aggregate
order moves to #cancelledIf 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 (api) suffix and aggregate creation are independent concepts:
(api)suffix = API/public endpoint (externally accessible)- Aggregate creation = determined by event position in flow (first event creates, subsequent events require reference)
| Event Type | API Endpoint? | Creates Aggregate? | Routing |
|---|---|---|---|
on :event (api) (first in flow) | Yes | Yes (new) | New aggregate_id generated |
on :event (api) (subsequent) | Yes | No | Requires aggregate reference, else error |
on :event (internal) | No | Depends on context | Conversation context → correct aggregate |
Example
machine: @order
scenario: checkout flow
// First event → creates new aggregate (order-abc123)
// Also an API endpoint (externally callable)
on :checkout from @customer (api)
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 (api)
? order is in #awaiting_payment
order moves to #cancelledRuntime 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: #shippedThis 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