Events Overview
EventFlow follows the Actor Model: every machine is an actor that communicates through asynchronous events. There is no direct method calling - only event emission and handling.
Core Concepts
Machine = Actor = Always listening for events
↓
Event arrives → Process → Back to listeningMachines are isolated units that:
- Have their own state
- React to incoming events
- Emit events to other machines
- Never directly access another machine's state
API Events vs Internal Events
API Events (on>)
Externally accessible events that can be triggered via HTTP API or external systems:
on> :checkout from @customer
// This can be triggered via API
order moves to #awaiting_paymentThe > prefix marks this event as a public endpoint.
Internal Events (on)
System-internal events that can only be triggered by other machines:
on :payment_success from @payment
// Only triggered by @payment machine
order moves to #paidNo > prefix - this event is internal to the system.
Receiving Events
Use on :event from @actor or on> :event from @actor:
machine: @order
scenario: checkout flow
// Public endpoint
on> :checkout from @customer
? cart is valid
emit :payment_request to @payment
order moves to #awaiting_payment
// Internal handlers
on :payment_success from @payment
order moves to #paid
emit :reserve_stock to @inventory
on :payment_failed from @payment
order moves to #failed
emit :checkout_failed to @customerThe from Clause
The from clause filters who can send the event:
// Accept from specific actor
on :payment_success from @payment
order moves to #paid
// Accept from any actor (no from clause)
on :status_update
update internal status
// Different handling based on source
on :approval from @manager
order moves to #manager_approved
on :approval from @director
order moves to #director_approvedSending Events
Use emit :event to @actor:
on> :checkout from @customer
? cart is valid
emit :payment_request to @payment
order moves to #awaiting_paymentEvents with Data
Events can carry typed data:
on> :add_item from @customer
$items adds $item
$total increases by $item.price
emit :item_added to @customer
with:
| field | type | value |
| item | object | $item |
| total | number | $total |Or without explicit types:
emit :payment_request to @payment
with:
| order_id | $order_id |
| amount | $total |Simple inline data:
emit :payment_request to @payment
with $order_id, $total, $currencyPure Async Model
There is no request-response pattern. Communication is always:
emit- send event, don't waiton- handle event when it arrives
machine: @order
scenario: payment flow
on> :checkout from @customer
emit :payment_request to @payment
order moves to #awaiting_payment
// doesn't wait for response
on :payment_success from @payment
// handles response when it arrives
order moves to #paidExecution Model
Event handlers execute synchronously - all actions within a handler complete as a single unit.
Event emission is asynchronous - emit queues the event and continues without waiting for the target machine to process it.
on> :checkout from @customer
// ─── Runs synchronously (single transaction) ───
? cart is valid
$order_id becomes uuid()
order moves to #awaiting_payment
// ─── Queued for async processing ───
emit :payment_request to @payment
emit :analytics_event to @analyticsThis follows the Actor Model: actors process messages independently and communicate through asynchronous message passing.
Event Flow Diagram
Event Patterns
Request-Response Pattern (via Events)
machine: @order
on> :checkout from @customer
emit :payment_request to @payment
order moves to #awaiting_payment
on :payment_success from @payment
order moves to #paid
on :payment_failed from @payment
order moves to #payment_failedFan-Out Pattern
on> :order_completed from @system
emit :update_inventory to @inventory
emit :send_confirmation to @notification
emit :record_sale to @analytics
emit :update_loyalty to @customerFan-In Pattern
on> :checkout from @customer
emit :check_payment to @payment
emit :check_fraud to @fraud
emit :check_inventory to @inventory
on :payment_ok from @payment
$payment_ready becomes true
? all_checks_passed
order moves to #confirmed
on :fraud_ok from @fraud
$fraud_ready becomes true
? all_checks_passed
order moves to #confirmed
on :inventory_ok from @inventory
$inventory_ready becomes true
? all_checks_passed
order moves to #confirmedSaga Pattern
machine: @order_saga
on> :start_order from @customer
emit :reserve_inventory to @inventory
saga moves to #reserving_inventory
on :inventory_reserved from @inventory
emit :process_payment to @payment
saga moves to #processing_payment
on :payment_success from @payment
emit :confirm_inventory to @inventory
emit :order_confirmed to @customer
saga moves to #completed
// Compensating actions
on :payment_failed from @payment
emit :release_inventory to @inventory
emit :order_failed to @customer
saga moves to #failedBest Practices
Use Descriptive Event Names
// Good - clear intent
emit :payment_authorization_requested to @payment
emit :order_shipment_prepared to @warehouse
// Avoid - vague
emit :request to @payment
emit :update to @warehouseHandle All Responses
on> :checkout from @customer
emit :payment_request to @payment
order moves to #awaiting_payment
on :payment_success from @payment
order moves to #paid
on :payment_failed from @payment
order moves to #payment_failed
on :payment_timeout from @payment
order moves to #payment_timeoutDocument Event Data
emit :order_details to @warehouse
with:
| field | type | value |
| order_id | string | $order_id |
| items | array | $items |
| priority | string | "standard" |
| ship_by | date | $ship_date |