Saga Pattern using Orchestration in Spring Boot | Complete Guide with Real-World Example
If you're preparing for Oracle, Walmart, Microsoft, Amazon, Uber, or other product-based company interviews, understanding the Orchestration Saga Pattern is extremely important. Unlike the Choreography Saga, where services…
If you’re preparing for Oracle, Walmart, Microsoft, Amazon, Uber, or other product-based company interviews, understanding the Orchestration Saga Pattern is extremely important.
Unlike the Choreography Saga, where services communicate by publishing events, the Orchestration Saga introduces a central coordinator called the Saga Orchestrator.
The orchestrator is responsible for:
- Calling each microservice
- Deciding what happens next
- Handling failures
- Triggering compensating transactions (rollback)
This makes the workflow much easier to understand and debug.
In this article we’ll build a complete Spring Boot implementation using REST APIs.
Why Do We Need Orchestration?
Imagine an ecommerce application.
A customer places an order.
To complete that order, multiple microservices must execute successfully.
- Order Service
- Inventory Service
- Payment Service
- Shipping Service
If any service fails, previously completed services must undo their work.
Without an orchestrator, every service would need to know which events to listen to and what to publish.
As the number of services grows, the workflow becomes difficult to understand.
Instead, we introduce one service whose only responsibility is coordinating the saga.
Our Use Case
Customer places an order.
There are five services.
- Saga Orchestrator
- Order Service
- Inventory Service
- Payment Service
- Shipping Service
Overall Flow
Client
|
â–¼
Saga Orchestrator
|
â–¼
Order Service
(Create Order)
|
Success
â–¼
Inventory Service
(Reserve Inventory)
|
Success
â–¼
Payment Service
(Process Payment)
|
Success
â–¼
Shipping Service
(Create Shipment)
|
Success
â–¼
Order Service
(Mark Completed)
Notice something important.
Every service only communicates with the Saga Orchestrator.
No service calls another service.
Failure Flow
Suppose payment fails.
Saga Orchestrator
|
â–¼
Order Created
|
â–¼
Inventory Reserved
|
â–¼
Payment Failed
|
â–¼
Call Inventory Compensation
Release Inventory
|
â–¼
Call Order Compensation
Cancel Order
The orchestrator decides every rollback.
Architecture
Client
|
â–¼
Saga Orchestrator
_________|_____|__________
| | | |
â–¼ â–¼ â–¼ â–¼
Order Inventory Payment Shipping
Service Service Service Service
Everything passes through the orchestrator.
Step 1
Client sends request
POST /orders
to
Saga Orchestrator
Order Request
public class OrderRequest {
private Long productId;
private int quantity;
private double amount;
}
Response Objects
Each service returns
public class ServiceResponse {
private boolean success;
private String message;
}
Instead of publishing Kafka events, services simply return success or failure.
Order Service
POST /orders
@PostMapping
public Order createOrder(
@RequestBody OrderRequest request){
Order order = new Order();
order.setStatus("CREATED");
repository.save(order);
return order;
}
Compensation API
DELETE /orders/{id}
@DeleteMapping("/{id}")
public void cancelOrder(
@PathVariable Long id){
Order order =
repository.findById(id).get();
order.setStatus("CANCELLED");
repository.save(order);
}
Notice
Order Service exposes two APIs.
- Business API
- Compensation API
Inventory Service
Reserve inventory
@PostMapping("/reserve")
public ServiceResponse reserveInventory(
@RequestBody InventoryRequest request){
Product product =
repository.findById(request.getProductId()).get();
if(product.getQuantity()
< request.getQuantity()){
return new ServiceResponse(false,
"Stock unavailable");
}
product.setQuantity(
product.getQuantity()-request.getQuantity());
repository.save(product);
return new ServiceResponse(true,
"Reserved");
}
Compensation
@PostMapping("/release")
public void releaseInventory(
@RequestBody InventoryRequest request){
Product product =
repository.findById(request.getProductId()).get();
product.setQuantity(
product.getQuantity()+request.getQuantity());
repository.save(product);
}
Payment Service
@PostMapping("/pay")
public ServiceResponse pay(
@RequestBody PaymentRequest request){
try{
paymentGateway.pay();
return new ServiceResponse(
true,
"Payment Successful");
}
catch(Exception e){
return new ServiceResponse(
false,
"Payment Failed");
}
}
Shipping Service
@PostMapping("/ship")
public ServiceResponse createShipment(
@RequestBody ShipmentRequest request){
Shipment shipment = new Shipment();
repository.save(shipment);
return new ServiceResponse(
true,
"Shipment Created");
}
Saga Orchestrator
This is the heart of the entire workflow.
@Service
public class OrderSagaOrchestrator {
@Autowired
private OrderClient orderClient;
@Autowired
private InventoryClient inventoryClient;
@Autowired
private PaymentClient paymentClient;
@Autowired
private ShippingClient shippingClient;
public void execute(OrderRequest request){
Order order =
orderClient.createOrder(request);
ServiceResponse inventory =
inventoryClient.reserve(request);
if(!inventory.isSuccess()){
orderClient.cancel(order.getId());
return;
}
ServiceResponse payment =
paymentClient.pay(request);
if(!payment.isSuccess()){
inventoryClient.release(request);
orderClient.cancel(order.getId());
return;
}
ServiceResponse shipping =
shippingClient.ship(request);
if(!shipping.isSuccess()){
paymentClient.refund(order.getId());
inventoryClient.release(request);
orderClient.cancel(order.getId());
return;
}
orderClient.complete(order.getId());
}
}
Notice something very important.
The orchestrator contains the entire business workflow.
Every decision is taken here.
Success Flow
Create Order
↓
Reserve Inventory
↓
Process Payment
↓
Create Shipment
↓
Complete Order
Payment Failure
Create Order
↓
Reserve Inventory
↓
Payment Failed
↓
Release Inventory
↓
Cancel Order
Shipping Failure
Create Order
↓
Reserve Inventory
↓
Payment Success
↓
Shipping Failed
↓
Refund Payment
↓
Release Inventory
↓
Cancel Order
Notice how compensation happens in reverse order of successful operations.
Why Is It Called Orchestration?
Think of an orchestra.
There is one conductor standing in front of the musicians.
The conductor tells:
- Who starts.
- Who stops.
- Who plays next.
Musicians don’t communicate with each other.
They simply follow the conductor.
The Saga Orchestrator works exactly like that.
- It tells Inventory Service when to reserve stock.
- It tells Payment Service when to charge the customer.
- It tells Shipping Service when to create a shipment.
- It decides when to execute rollback.
Advantages
- Easy to understand
- Easy debugging
- Centralized business logic
- Easier compensation handling
- Simple monitoring
- Good for complex workflows
Disadvantages
- Single point of orchestration (must be made highly available)
- Orchestrator becomes more complex as workflows grow
- Slightly tighter coupling because the orchestrator knows every service
- Every new business step requires orchestrator changes
Orchestration vs Choreography
| Feature | Orchestration | Choreography |
|---|---|---|
| Coordinator | Central Saga Orchestrator | No coordinator |
| Communication | REST / Kafka Commands | Kafka Events |
| Workflow | Centralized | Distributed |
| Debugging | Easy | Difficult |
| Coupling | Higher | Lower |
| Scalability | Good | Excellent |
| Adding new service | Modify orchestrator | Subscribe to an event |
| Failure handling | Orchestrator invokes compensation | Services react to failure events |
| Best for | Business workflows with clear control | Large event-driven architectures |
Production Improvements
A real-world orchestrator should also include:
- Feign Clients or WebClient instead of
RestTemplatefor inter-service communication. - Timeouts and Circuit Breakers (e.g., Resilience4j) to prevent one slow service from blocking the saga.
- Retry Policies with exponential backoff for transient failures.
- Persistent Saga State, storing the current step and status in a database so the orchestrator can recover after a crash.
- Correlation IDs to trace a single saga across all services.
- Idempotent Compensation APIs, ensuring repeated rollback requests don’t produce inconsistent data.
- Asynchronous Commands via Kafka if you want centralized orchestration without blocking HTTP calls (hybrid orchestration).
Interview Tip
Many candidates think Orchestration always means REST and Choreography always means Kafka.
That’s not true.
The real difference is who controls the workflow:
- Orchestration → A central orchestrator decides the next step. It can communicate using REST, gRPC, Kafka commands, or other messaging systems.
- Choreography → There is no central coordinator. Services react independently to published events, typically through Kafka or another event broker.
So the distinction is about control flow, not the communication protocol.
This version is interview-ready and mirrors the style of your choreography article. It also naturally sets up a follow-up article demonstrating Orchestration Saga with Kafka, where the orchestrator still coordinates the process but sends commands and receives replies asynchronously through Kafka instead of synchronous REST calls—a pattern used in many large-scale production systems.