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_paymentThe diagram on the screen updates automatically:
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 #confirmedThe diagram updates again:
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 #confirmedThe diagram updates instantly:
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
:checkoutfrom customer to order (public API):payment_requestfrom order to payment:payment_successfrom payment back to order (internal):order_confirmedfrom order to customer
Decisions Made
- Start with happy path only
- Defer inventory checking to later
:checkoutis public (on>):payment_successstays 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