Skip to content

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 listening

Machines 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:

flow
on> :checkout from @customer
  // This can be triggered via API
  order moves to #awaiting_payment

The > prefix marks this event as a public endpoint.

Internal Events (on)

System-internal events that can only be triggered by other machines:

flow
on :payment_success from @payment
  // Only triggered by @payment machine
  order moves to #paid

No > prefix - this event is internal to the system.

Receiving Events

Use on :event from @actor or on> :event from @actor:

flow
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 @customer

The from Clause

The from clause filters who can send the event:

flow
// 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_approved

Sending Events

Use emit :event to @actor:

flow
on> :checkout from @customer
  ? cart is valid
    emit :payment_request to @payment
    order moves to #awaiting_payment

Events with Data

Events can carry typed data:

flow
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:

flow
emit :payment_request to @payment
  with:
    | order_id | $order_id |
    | amount   | $total    |

Simple inline data:

flow
emit :payment_request to @payment
  with $order_id, $total, $currency

Pure Async Model

There is no request-response pattern. Communication is always:

  1. emit - send event, don't wait
  2. on - handle event when it arrives
flow
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 #paid

Execution 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.

flow
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 @analytics

This follows the Actor Model: actors process messages independently and communicate through asynchronous message passing.

Event Flow Diagram

@customer@order@payment:checkout:payment_request(processes):payment_success:order_confirmed

Event Patterns

Request-Response Pattern (via Events)

flow
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_failed

Fan-Out Pattern

flow
on> :order_completed from @system
  emit :update_inventory to @inventory
  emit :send_confirmation to @notification
  emit :record_sale to @analytics
  emit :update_loyalty to @customer

Fan-In Pattern

flow
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 #confirmed

Saga Pattern

flow
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 #failed

Best Practices

Use Descriptive Event Names

flow
// Good - clear intent
emit :payment_authorization_requested to @payment
emit :order_shipment_prepared to @warehouse

// Avoid - vague
emit :request to @payment
emit :update to @warehouse

Handle All Responses

flow
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_timeout

Document Event Data

flow
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  |

Released under the MIT License.