An e-commerce order isn’t a single event; it’s a chain reaction of independent services that must coordinate perfectly, or everything falls apart.
Let’s see this in action with a hypothetical order for "The Amazing Widget" (SKU: AW1001) from our example store.
1. Inventory Service:
POST /api/inventory/reserve
{
"sku": "AW1001",
"quantity": 2,
"reservation_id": "ORD-987654"
}
Response:
{
"sku": "AW1001",
"reserved_quantity": 2,
"available_quantity": 98,
"reservation_id": "ORD-987654"
}
The inventory service confirms we have two widgets and "holds" them against ORD-987654. This isn’t a permanent deduction, just a temporary reservation to prevent overselling.
2. Payment Service:
POST /api/payment/charge
{
"order_id": "ORD-987654",
"amount": 199.98,
"currency": "USD",
"payment_method_token": "tok_abcdef12345"
}
Response:
{
"charge_id": "CHG-567890",
"order_id": "ORD-987654",
"status": "succeeded",
"amount": 199.98
}
If payment succeeds, the order_id links the transaction to the inventory reservation.
3. Shipping Service:
POST /api/shipping/create_label
{
"order_id": "ORD-987654",
"destination": {
"name": "Jane Doe",
"address1": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "90210",
"country": "US"
},
"package_dimensions": {
"length": 10,
"width": 8,
"height": 4,
"unit": "inches"
},
"weight": {
"value": 1.5,
"unit": "lbs"
}
}
Response:
{
"shipping_id": "SHIP-ABCDEFG",
"order_id": "ORD-987654",
"tracking_number": "1Z999AA10123456784",
"status": "created"
}
The shipping service uses the order details to generate a label and tracking number.
4. Finalizing Inventory & Order:
Now, the system needs to tell the inventory service that the payment and shipping are confirmed.
POST /api/inventory/commit
{
"sku": "AW1001",
"quantity": 2,
"reservation_id": "ORD-987654"
}
Response:
{
"sku": "AW1001",
"deducted_quantity": 2,
"final_available_quantity": 98
}
And finally, mark the order itself as complete.
POST /api/orders/complete
{
"order_id": "ORD-987654",
"status": "completed"
}
This entire sequence is a "saga" – a series of local transactions. If any step fails, compensating transactions are triggered. For example, if payment fails, the inventory reservation is released. If shipping fails, the payment might be refunded, and the inventory reservation might be cancelled.
The real magic of a saga pattern is managing these failures. Each service owns its part of the overall business process. The Inventory service doesn’t know how payment works, and the Payment service doesn’t care about shipping logistics. They only care about their specific piece and signalling success or failure to the orchestrator (or to each other, in a choreography-based saga). This loose coupling allows services to evolve independently, but it places a heavy burden on the error handling and retry logic. A common pitfall is assuming a simple retry will fix transient network issues without considering the state of other services; a payment might succeed on retry, but the inventory might have been released in the interim, leading to an oversell.
The surprising truth is that the most robust systems often have the simplest individual services, relying on the saga orchestrator or choreography to stitch them together reliably. The complexity isn’t in what each service does, but in the state transitions and failure handling across the entire distributed system. The reservation_id is the critical piece of data that ties all these disparate services together. Without it, the system has no way to correlate an inventory reservation with a payment charge or a shipping label request.
The next problem you’ll likely encounter is handling partial fulfillment when a customer orders multiple items and only some are available or can be shipped.