Create order
Order creation flow: from the frontend to the orders microservice, with event publishing and coordination with inventory and payments.
Why order creation is a complex flow
Creating an order is not simply saving a record to a database. It’s a flow that involves multiple services, business validations, inventory reservation, payment initiation, and notifications. It’s the perfect example of how synchronous and asynchronous communication coexist within a single use case.
The complete flow
sequenceDiagram
participant FE as Frontend
participant GW as API Gateway
participant BFF as BFF
participant ORD as ms-orders
participant DB as Orders DB
participant EB as Event Bus
participant INV as ms-inventory
participant PAY as ms-payments
participant NOT as ms-notifications
FE->>GW: POST /orders
GW->>GW: Auth, rate limiting
GW->>BFF: Forward
BFF->>ORD: Crear orden
ORD->>ORD: Validar datos de la orden
ORD->>DB: Guardar orden (estado: pending)
DB-->>ORD: OK
ORD->>EB: Publicar OrderPlaced
ORD-->>BFF: Orden creada (id, estado)
BFF-->>GW: Respuesta para UI
GW-->>FE: 201 Created + orden
Note over EB: Procesamiento asíncrono
EB->>INV: OrderPlaced
INV->>INV: Reservar stock
INV->>EB: InventoryReserved
EB->>PAY: OrderPlaced
PAY->>PAY: Iniciar proceso de pago
EB->>NOT: OrderPlaced
NOT->>NOT: Enviar confirmación al usuario
Synchronous phase: creating the order
The first part of the flow is synchronous. The user is waiting for confirmation that their order was received.
Frontend
- The user reviews their cart and confirms the purchase
- The frontend sends the order data: items, quantities, shipping address, payment method
- It shows a spinner while waiting for confirmation
- It receives the confirmation with the order ID and displays a success message
BFF
- Receives the data from the frontend
- Can enrich the request (resolve product IDs, validate format)
- Invokes the orders microservice
- Transforms the response for the frontend
Orders microservice (ms-orders)
This is the service that orchestrates the creation:
- Validates the order data (items exist, quantities valid, address complete)
- Calculates the total
- Saves the order to its database with status
pending - Publishes the
OrderPlacedevent to the Event Bus - Returns the created order to the BFF
Key point: the order is saved and the event is published in the same operation (ideally using the Outbox Pattern to guarantee consistency).
Asynchronous phase: coordination between services
Once the OrderPlaced event is published, several services react independently.
Inventory (ms-inventory)
- Receives the
OrderPlacedevent - Verifies there is enough stock for each item
- Reserves the stock (marks it as committed)
- Publishes
InventoryReservedorInventoryInsufficient
Payments (ms-payments)
- Receives the
OrderPlacedevent - Starts the charging process with the payment provider
- Publishes
PaymentCompletedorPaymentFailed
Notifications (ms-notifications)
- Receives the
OrderPlacedevent - Sends a confirmation email to the user
- Can send a push notification if the app supports it
Coordination and order states
The order goes through several states depending on the events it receives:
stateDiagram-v2
[*] --> Pending: OrderPlaced
Pending --> Confirmed: InventoryReserved + PaymentCompleted
Pending --> Cancelled: InventoryInsufficient
Pending --> Cancelled: PaymentFailed
Confirmed --> Shipped: ShipmentCreated
Shipped --> Delivered: ShipmentDelivered
Cancelled --> [*]
Delivered --> [*]
The orders microservice listens to the inventory and payment events to update the state:
- If both are successful →
confirmed - If inventory fails →
cancelled(and the payment is reverted if it was already charged) - If payment fails →
cancelled(and the reserved inventory is released)
The consistency problem
In this flow, there is no distributed transaction guaranteeing that everything happens atomically. Instead, eventual consistency is used:
- The order is created in
pendingstatus - The services process their parts independently
- The result events update the order’s state
- If something fails, compensations are executed (release stock, revert payment)
This is the Saga pattern in action: a sequence of local transactions coordinated by events.
Compensations
When something fails after other services have already acted, compensations are needed:
| Failure | Compensation |
|---|---|
| Payment fails after reserving inventory | Publish PaymentFailed → inventory releases stock |
| Insufficient inventory after starting payment | Publish InventoryInsufficient → payments cancels the charge |
| Timeout with no response from inventory | Order moves to cancelled after a time limit |
Idempotency
Each consumer must be idempotent. If the OrderPlaced event is delivered twice:
- Inventory must not reserve stock twice
- Payments must not charge twice
- Notifications must not send two emails
The most common way to achieve this is to store the ID of the processed event and check it before acting.
What the user sees
From the user’s perspective:
- They click “Confirm purchase”
- They see a spinner for 1–2 seconds
- They see “Order #12345 received” with status “Processing”
- They receive a confirmation email
- They can check the order status, which updates as the services process it
The user doesn’t need to know that behind the scenes there are 4 services coordinating. They just see a clean flow.
Summary
Creating an order combines synchronous communication (to give the user a fast response) with asynchronous communication (to coordinate inventory, payments, and notifications). Consistency is achieved through events and compensations, not through distributed transactions. Each service is responsible for its part and reacts to the events of the others.