Event Simulator
Design and trace event flows between microservices β from publication to consumption and chain reactions.
Objective
In this lab you will design event flows between microservices, trace their complete journey, and anticipate the problems that can arise in an event-driven architecture.
Events are the nervous system of a distributed architecture. Understanding how they flow is essential to designing decoupled and resilient systems.
Prerequisites
Before you start, make sure you understand the difference between:
| Concept | Description | Example |
|---|---|---|
| Command | A request to do something | CreateOrder |
| Event | A notification that something already happened | OrderCreated |
| Query | A request for information | GetOrderStatus |
Events are immutable facts β something that already happened and cannot be undone. Commands are intentions β they can be rejected.
Exercise 1: Design an event flow
Scenario
A user buys a product in your e-commerce store. Design the complete event flow from the moment they click βBuyβ until they receive the confirmation.
Services involved
- Orders Service: creates and manages orders
- Inventory Service: reserves and deducts stock
- Payments Service: processes the charge
- Notifications Service: sends emails and push notifications
- Analytics Service: records business metrics
Your task
Complete the event diagram:
User clicks "Buy"
β
βΌ
[Orders Service]
β publishes: OrderCreated { orderId, userId, items, total }
β
ββββΊ [Inventory Service]
β consumes: OrderCreated
β action: reserve stock
β publishes: _______________
β
ββββΊ [Payments Service]
β consumes: _______________
β action: process charge
β publishes: _______________
β
ββββΊ [Notifications Service]
β consumes: _______________
β action: _______________
β
ββββΊ [Analytics Service]
consumes: _______________
action: _______________
Solution
View full flow
User clicks "Buy"
β
βΌ
[Orders Service]
β publishes: OrderCreated { orderId, userId, items, total }
β
ββββΊ [Inventory Service]
β consumes: OrderCreated
β action: reserve stock for the items
β publishes: StockReserved { orderId, items }
β or StockInsufficient { orderId, items }
β
ββββΊ [Payments Service]
β consumes: StockReserved
β action: process charge with the provider
β publishes: PaymentCompleted { orderId, transactionId }
β or PaymentFailed { orderId, reason }
β
ββββΊ [Notifications Service]
β consumes: PaymentCompleted
β action: send confirmation email to the user
β consumes: PaymentFailed
β action: send failure email to the user
β
ββββΊ [Analytics Service]
consumes: OrderCreated, StockReserved, PaymentCompleted
action: record conversion and revenue metrics
Exercise 2: Handling the unhappy path
Scenario
What happens when something fails in the middle of the flow? Design the compensations.
Case 1: Stock was reserved but payment failed
OrderCreated β StockReserved β PaymentFailed
β
βΌ
What happens now?
Your task: Which event should the payments service publish? Who consumes it? What action does it take?
View solution
PaymentFailed { orderId, reason }
β
ββββΊ [Inventory Service]
β consumes: PaymentFailed
β action: release reserved stock
β publishes: StockReleased { orderId, items }
β
ββββΊ [Orders Service]
β consumes: PaymentFailed
β action: mark order as failed
β publishes: OrderCancelled { orderId, reason }
β
ββββΊ [Notifications Service]
consumes: OrderCancelled
action: notify the user that the payment failed
This is the Saga with choreography pattern: each service reacts to events and runs compensations when something fails. There is no central orchestrator.
Case 2: The inventory service is down
OrderCreated β [Inventory: DOWN] β ???
Your task: What happens to the OrderCreated event? Is it lost? Is it retried?
View solution
If you use a message broker with durable queues (RabbitMQ, Kafka):
- The
OrderCreatedevent stays in the queue waiting - When the inventory service comes back up, it consumes the pending events
- Processing continues normally (with delay)
Key point: The queue acts as a temporary buffer. The event is not lost as long as the queue is durable and the messages are persistent.
Consideration: If the service is down for a long time, the queue grows. You need:
- Alerts based on queue size
- TTL on messages to avoid processing very old events
- Compensation logic if the event is no longer relevant
Exercise 3: Event ordering
The problem
Two events arrive at the inventory service:
StockUpdated { productId: "ABC", quantity: 100 }(timestamp: 10:00:01)StockUpdated { productId: "ABC", quantity: 50 }(timestamp: 10:00:00)
Event 2 was published first but arrived later. If you process in arrival order, the stock ends up at 50 (incorrect β it should be 100).
Your task
How do you guarantee the correct order? Evaluate these strategies:
| Strategy | Pros | Cons |
|---|---|---|
| Sort by timestamp | Simple | Clocks not synchronized across services |
| Sequence number | Guaranteed order | Requires coordination between publishers |
| Partition by key | Order within the partition | Limits parallelism |
| Optimistic versioning | No global order required | Requires conflict resolution logic |
View recommendation
The most practical strategy for most cases is partition by key:
- In Kafka: use
productIdas the partition key - All events for the same product go to the same partition
- Within a partition, order is guaranteed
- Different products are processed in parallel
For cases where you need global order, use a sequence number generated by the publisher and reject events with a sequence lower than the last one processed.
Exercise 4: Design your own flow
Your task
Pick one of these scenarios and design the complete event flow:
Option A: Hotel booking system
- Services: Bookings, Rooms, Payments, Notifications
- Flow: user books β check availability β charge β confirm
Option B: Delivery platform
- Services: Orders, Restaurants, Riders, Tracking, Notifications
- Flow: user orders β restaurant accepts β rider assigned β delivery
Option C: Subscription system
- Services: Users, Subscriptions, Payments, Access, Notifications
- Flow: user subscribes β recurring charge β renewal/cancellation
For each one, define:
- The main events (happy path)
- The compensation events (unhappy path)
- Which service publishes and who consumes each event
- How you handle failures at each step
Reflection
- How many events did you need for a βsimpleβ flow?
- How difficult was it to design the compensations?
- Would you prefer a central orchestrator (orchestrated Saga) or choreography?
- How would you debug a problem in a flow of 5+ events?
Event-driven systems are powerful but complex. The key is to design the flows before writing code β and always think about what happens when something fails.