Skip to content

Getting Started

This guide will walk you through creating your first EventFlow file.

Your First Flow

Create a file named order.flow:

flow
machine: @order

scenario: simple checkout

  on> :checkout from @customer
    order moves to #awaiting_payment

  on :payment_received
    order moves to #paid

  expect:
    = order is in #paid

That's it! You've defined a state machine with:

  • Two states: #awaiting_payment and #paid
  • Two events: :checkout and :payment_received
  • A test assertion

Understanding the Structure

Let's break down each part:

Machine Declaration

flow
machine: @order

This declares an actor named @order. The @ prefix indicates this is an actor (machine). Every .flow file starts with a machine declaration.

Scenario

flow
scenario: simple checkout

A scenario groups related event handlers. Think of it as a feature or capability of your machine.

Event Handlers

flow
on> :checkout from @customer
  order moves to #awaiting_payment
  • on> - This is an API endpoint (externally accessible)
  • :checkout - The event name (: prefix)
  • from @customer - Who can send this event
  • order moves to #awaiting_payment - State transition (# prefix for states)

Internal Events

flow
on :payment_received
  order moves to #paid

Without the > prefix, this event is internal - it can only be received from other machines, not called directly as an API.

Assertions

flow
expect:
  = order is in #paid

The expect: block defines test assertions. The = prefix marks assertion lines.

Adding Guards

Guards are conditions that must be true for an action to execute:

flow
machine: @order

scenario: checkout with validation

  on> :checkout from @customer
    ? cart is not empty
      order moves to #awaiting_payment
      emit :payment_request to @payment
    ?
      emit :error to @customer
  • ? prefix introduces a guard (condition)
  • Empty ? acts as "otherwise" (else case)

You can also use the otherwise keyword:

flow
  on> :checkout from @customer
    ? cart is not empty
      order moves to #awaiting_payment
    otherwise
      emit :empty_cart_error to @customer

Working with Context

Context holds your machine's data:

flow
machine: @cart

scenario: add items

  on> :add_item from @customer
    $items adds $item
    $total increases by $item.price
    $item_count increases by 1

  on> :remove_item from @customer
    $items removes $item
    $total decreases by $item.price
    $item_count decreases by 1

  on> :clear_cart from @customer
    $items becomes empty
    $total becomes 0
    $item_count becomes 0
  • $ prefix indicates context variables
  • Natural language operations: adds, removes, increases by, becomes

Communicating Between Machines

Machines communicate through events:

flow
machine: @order

scenario: checkout flow

  on> :checkout from @customer
    order moves to #awaiting_payment
    emit :payment_request to @payment    // Send to payment machine

  on :payment_success from @payment      // Receive from payment machine
    order moves to #paid
    emit :confirmation to @customer

The emit keyword sends events to other machines. The runtime automatically handles event routing and correlation.

Adding Test Setup

Use given: blocks to set up test scenarios:

flow
machine: @order

scenario: checkout with items

  given:
    @customer is logged in
    cart contains:
      | product | price |
      | Laptop  | 1200  |
      | Mouse   | 25    |
    $total is 1225

  on> :checkout from @customer
    ? cart is not empty
      order moves to #awaiting_payment

  expect:
    = order is in #awaiting_payment

Running Your Flow

Run Tests

bash
eventflow test order.flow

Generate Diagrams

bash
eventflow diagram order.flow

Validate Syntax

bash
eventflow validate order.flow

Complete Example

Here's a complete order flow with all concepts:

flow
machine: @order

scenario: complete checkout

  given:
    @customer is logged in as "[email protected]"
    cart has items
    $total: number is 1200

  on> :checkout from @customer
    ? cart is not empty
      ? $total > 0
        order moves to #awaiting_payment
        emit :payment_request to @payment
          with $total
      otherwise
        emit :zero_total_error to @customer
    otherwise
      emit :empty_cart_error to @customer

    expect:
      = order is in #awaiting_payment

  on :payment_success from @payment
    order moves to #paid
    $paid_at becomes now
    emit :order_confirmed to @customer
    emit :prepare_shipment to @warehouse

  on :payment_failed from @payment
    order moves to #payment_failed
    emit :payment_retry to @customer

  expect:
    = order is in #paid
    = @customer received :order_confirmed

Released under the MIT License.