When we start talking about managing business processes, it becomes clear that Saga is not just another pattern from a textbook. It's a lifeline for complex flows that involve multiple microservices. Picture an online store: a user places an order, the system checks inventory, reserves items, charges money, and notifies the delivery team. Each step has its own quirks — from external APIs to DB interactions. What if something goes wrong at some stage? Money charged but items not reserved? That's where the Saga steps in.
Saga is exactly what helps keep transactions consistent when failures happen. It's like a dance where two partners follow certain moves: if one slips, the other helps get back in rhythm. In this case that "dance" is about data and business logic.
Integrating Saga into business processes
Saga is most useful in processes that:
- Involve multiple microservices, each doing its part of the transaction.
- Require actions to be done in a specific sequence.
- Must guarantee rollback of operations on error.
An example is the order processing flow we already mentioned. Let's break it down in more detail.
Let's take a slightly idealized (but realistic) example. The order process looks like this:
- Order creation — the user picks an item and places an order.
- Inventory check — the system checks stock availability.
- Product reservation — if the item is available, it's reserved.
- Charge payment — the system charges the customer's card.
- Notify delivery — the order is handed off to the delivery team.
Each of these steps can be handled by different microservices. Saga helps make the flow reliable: if the "Product reservation" step fails (e.g., item sold out), you can roll back earlier operations (for example, cancel the order).
How Saga helps manage business processes
Saga helps to:
- Coordinate processes between microservices. Instead of writing hairy orchestration code by hand, you define steps and compensating actions for each transaction.
- Provide fault tolerance. If one step can't be completed, the saga triggers compensating actions to bring the system back to a stable state.
- Reduce development time. A standardized saga approach lets you design reliable flows faster.
What a Saga consists of
- Steps — actions performed as part of the transaction. For example, "reserve product".
- Compensating steps — actions that undo changes made by previous steps. For example, "cancel product reservation".
- Saga Manager — the component (or library) that controls step execution. It decides which actions run next or which step to compensate in case of an error.
Example
Suppose we're designing an order management system for an online store. Requirements:
- All steps must complete successfully: order creation, product reservation, charging payment, notifying delivery.
- All operations must be rolled back if an error occurs at any step.
System design
Here's a diagram of microservice interactions using the Saga pattern:
[Order Service] -> Create order
v
[Inventory Service] -> Reserve product
v
[Payment Service] -> Charge payment
v
[Delivery Service] -> Notify delivery service
Compensating actions in case of errors:
- If product reservation fails, the order in
Order Serviceis canceled. - If charging payment fails, the product reservation is released.
- If notifying the delivery team fails, the money is refunded to the user.
Saga implementation
Step 1: defining steps and compensations
Here's example code for the product reservation step:
public class InventoryService {
public void reserveProduct(String productId, int quantity) {
// Logic for reserving the product
System.out.println("Reserving product: " + productId);
}
public void cancelReservation(String productId, int quantity) {
// Logic to cancel the reservation
System.out.println("Cancelling product reservation: " + productId);
}
}
Step 2: the Saga Manager
For orchestration we'll use a hand-written Saga manager.
public class SagaManager {
private final List<SagaStep> sagaSteps = new ArrayList<>();
public void addStep(SagaStep step) {
sagaSteps.add(step);
}
public void execute() {
Stack<SagaStep> executedSteps = new Stack<>();
try {
for (SagaStep step : sagaSteps) {
step.perform();
executedSteps.push(step);
}
} catch (Exception e) {
while (!executedSteps.isEmpty()) {
executedSteps.pop().compensate();
}
}
}
}
Step 3: defining steps in the Saga
Each step implements the SagaStep interface.
public interface SagaStep {
void perform();
void compensate();
}
// Example of a step implementation
public class ReserveInventoryStep implements SagaStep {
private final InventoryService inventoryService;
private final String productId;
private final int quantity;
public ReserveInventoryStep(InventoryService inventoryService, String productId, int quantity) {
this.inventoryService = inventoryService;
this.productId = productId;
this.quantity = quantity;
}
@Override
public void perform() {
inventoryService.reserveProduct(productId, quantity);
}
@Override
public void compensate() {
inventoryService.cancelReservation(productId, quantity);
}
}
Step 4: running the Saga
Now let's put everything together and run the Saga.
public class SagaExample {
public static void main(String[] args) {
InventoryService inventoryService = new InventoryService();
SagaManager sagaManager = new SagaManager();
sagaManager.addStep(new ReserveInventoryStep(inventoryService, "product-123", 2));
// Add other steps here (e.g., charging payment, notifying delivery)
sagaManager.execute();
}
}
Business benefits
A system built with Saga:
- Supports scalability. You can add new steps without breaking existing logic.
- Ensures data consistency even under failures.
- Provides flexibility. Choosing between orchestration and choreography lets you adapt to different scenarios.
For example, Amazon, Uber, and other big companies actively use Sagas to manage critical business processes. If it works for them, it'll be handy for you too!
Now that you understand how the Saga pattern is applied, you're ready to move to hands-on practice and start building truly resilient business processes! Productive practice!
GO TO FULL VERSION