Skip to content

Session 1: Discovery

The team joins their first Event Flowing session. The goal: understand the checkout flow and identify actors, events, and the data that flows between them.

Setting the Scene

Everyone joins the video call. Alex shares their screen showing the editor with an empty order.flow file on one side and the live diagram on the other. EventFlow runs in watch mode - every change updates the diagram automatically.

bash
$ eventflow watch order.flow

  EventFlow v1.0.0

  Editor:   http://localhost:5173/
  Diagram:  http://localhost:5173/diagram
  Tests:    http://localhost:5173/tests

  Watching for changes...
We need to build the checkout flow. The basic journey is: customer adds items to cart, clicks checkout, pays, and receives a confirmation. Let's map this out.
What states does the user see during this process? I need to design the UI for each step.
Good question. They should see their order status change: something like 'pending', then 'processing', then 'paid'.
Let me start writing this in EventFlow. We can watch the diagram update as we go.
Before we dive in - what can go wrong? Payment could fail, inventory could be out of stock...
Valid concerns. Let's note those down, but start with the happy path first. We'll clarify what could go wrong as we go, then tackle them in a dedicated edge case session.

Writing the First Lines

Alex starts typing. Everyone watches the shared screen as the code and diagram update together.

First, let's identify who's involved. I see a customer, an order system, and a payment system.
flow
// order.flow
machine: @order
The order is the main thing we're tracking. Customers interact with it, payments affect it.
Right. And we need @payment as a separate actor - it's an external service. Let me add some context about what we're building.
flow
machine: @order

// Actors involved:
// @customer - the buyer
// @order - this machine (order management)
// @payment - external payment processing service
What about inventory? Don't we need to check stock?
Eventually, yes. But Sarah said happy path first?
Correct. Let's assume inventory is always available for now. We can add @inventory later.

Defining the First Event

So the flow starts when a customer initiates checkout. Let me define this event first as internal - we can make it public later when we're sure about the structure.
flow
machine: @order

scenario: checkout

  on :checkout from @customer
No `>` symbol - so this is an internal event for now?
Yes. We design first, then decide what becomes a public API. Let me add what should happen when checkout is triggered.
When checkout happens, we need to request payment from the payment service.
Let me add that with some context variables. We'll need to track the order amount.
flow
machine: @order

scenario: checkout

  given:
    $total: number is calculated

  on :checkout from @customer
    emit :payment_request to @payment
    order moves to #awaiting_payment

The diagram on the screen updates automatically:

@customer@order@payment:checkout:payment_request
I can see the flow now. Customer sends checkout, order requests payment.
But what happens after payment? Where's the response?
Good catch. We need to handle the payment result.

Adding the Payment Response

When payment succeeds, we confirm the order. The customer should receive a confirmation.
I'll also add an order ID. We need to track which order we're dealing with.
flow
machine: @order

scenario: checkout

  given:
    $total: number is calculated

  on :checkout from @customer
    $order_id: string becomes uuid()
    emit :payment_request to @payment
      with $order_id, $total
    order moves to #awaiting_payment

  on :payment_success from @payment
    send confirmation email
    order moves to #confirmed

The diagram updates again:

@customer@order@payment:checkoutgenerate $order_id:payment_request($order_id, $total)(processes payment):payment_successsend confirmation email
I like that we're tracking the order ID now. The payment service can use it to correlate the transaction.
The `with` keyword passes data along with the event. It's typed - $order_id is a string, $total is a number.
The diagram is missing something - shouldn't the customer receive the confirmation somehow? Right now the email is sent but the UI doesn't know.
Good point! The email is an action, but we should also emit an event so the frontend knows to update.
Yes, the frontend needs to update. Add that.
flow
  on :payment_success from @payment
    send confirmation email
    emit :order_confirmed to @customer
      with $order_id
    order moves to #confirmed

The diagram updates instantly:

@customer@order@payment:checkoutgenerate $order_id:payment_request($order_id, $total)(processes payment):payment_successsend confirmation email:order_confirmed($order_id)
Now the diagram shows the complete communication. The frontend can listen for :order_confirmed to update the UI.
This is exactly what I had in mind. I can show this diagram to stakeholders and they'll understand it immediately.

Making the Event Public

Now that we understand the flow, should :checkout be a public API?
Yes, customers need to trigger it externally. Let me add the `>` symbol.
flow
  on> :checkout from @customer
The `>` after `on` marks this as a public endpoint. External systems can call it.
And :payment_success stays internal because only @payment can send it?
Exactly. Internal events are more secure - they can only come from known actors.

Adding Expectations

We should add assertions so this becomes a test. What should be true after each step?
Good idea. Let me add expect: blocks to verify the outcomes.
flow
machine: @order

scenario: checkout

  given:
    @customer is logged in
    cart has items
    $total: number is calculated

  on> :checkout from @customer
    $order_id: string becomes uuid()
    emit :payment_request to @payment
      with $order_id, $total
    order moves to #awaiting_payment

    expect:
      = order is in #awaiting_payment
      = @payment received :payment_request

  on :payment_success from @payment
    send confirmation email
    emit :order_confirmed to @customer
      with $order_id
    order moves to #confirmed

  expect:
    = order is in #confirmed
    = @customer received :order_confirmed
The `=` prefix means assertion. We're verifying the state and that events were received.
What about those given: conditions? What if the customer isn't logged in?
Good point. The given: block is for test setup - it tells the test framework what world to create. But we need runtime guards for the actual logic.
Let's handle those edge cases in the next session. For now we have our happy path documented.

Running the First Test

Let me run the test to make sure our flow is valid.
bash
$ eventflow test order.flow

@order / checkout

  happy path
 checkout awaiting_payment (12ms)
 payment_success confirmed (15ms)

2 passing
Tests pass. Our flow is executable.
Perfect. We have a working happy path. Let's take a break and come back for edge cases.

Session Outcome

What We Built

  • Basic checkout flow with customer, order, and payment actors
  • Payment request/response cycle
  • Confirmation email and event to customer
  • Typed context variables ($order_id: string, $total: number)

The Diagram Shows

  • :checkout from customer to order (public API)
  • :payment_request from order to payment
  • :payment_success from payment back to order (internal)
  • :order_confirmed from order to customer

Decisions Made

  • Start with happy path only
  • Defer inventory checking to later
  • :checkout is public (on>)
  • :payment_success stays internal (on)

Edge Cases Identified (to clarify, not solve yet)

  • What if cart is empty?
  • What if customer is not logged in?
  • What if payment fails?

These will be systematically addressed in Session 3 with QA.

The Flow So Far

flow
// order.flow v1
machine: @order

scenario: checkout

  given:
    @customer is logged in
    cart has items
    $total: number is calculated

  on> :checkout from @customer
    $order_id: string becomes uuid()
    emit :payment_request to @payment
      with $order_id, $total
    order moves to #awaiting_payment

    expect:
      = order is in #awaiting_payment
      = @payment received :payment_request

  on :payment_success from @payment
    send confirmation email
    emit :order_confirmed to @customer
      with $order_id
    order moves to #confirmed

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

Released under the MIT License.