- Java Microservices Tutorial
- Java Microservices - Home
- Microservices - Introduction
- Microservices vs Monolith vs SOA
- Java Microservices - Environment Setup
- Java Microservices - Advantages of Spring Boot
- Java Microservices - Design Patterns
- Java Microservices - Domain Driven Design
- Java Microservices - Decomposition by Business Capability
- Java Microservices - Decomposition by Subdomain
- Java Microservices - Backend for Frontend
- Java Microservices - The Strangler Pattern
- Java Microservices - Synchronous Communication
- Java Microservices - Asynchronous Communication
- Java Microservices - Saga Pattern
- Java Microservices - Centralized Logging (ELK Stack)
- Java Microservices - Event Sourcing
- Java Microservices - CQRS Pattern
- Java Microservices - Sidecar Pattern
- Java Microservices - Service Mesh Pattern
- Java Microservices - Circuit Breaker Pattern
- Java Microservices - Distributed Tracing
- Java Microservices - Control Loop Pattern
- Java Microservices - Database Per Service
- Java Microservices - Bulkhead Pattern
- Java Microservices - Health Check API
- Java Microservices - Retry Pattern
- Java Microservices - Fallback Pattern
- Java Microservices Useful Resources
- Java Microservices Quick Guide
- Java Microservices Useful Resources
- Java Microservices Discussion
Java Microservices - Saga Pattern
Introduction
As businesses embrace microservices architecture, one major challenge arises: how to maintain data consistency across distributed services. In traditional monoliths, a database transaction ensures ACID properties. But in microservices, each service often manages its own database − making distributed transactions difficult.
The Saga pattern is a solution to this problem. It allows services to collaborate on a long-running business transaction by exchanging a sequence of local transactions and compensating actions when needed.
This article explores the Saga pattern in detail, including its types, real-world examples, implementation with Spring Boot, and best practices.
What is Saga Pattern?
A Saga is a sequence of local transactions, where each transaction updates data within a single microservice and publishes an event or calls the next service. If one transaction fails, the Saga executes compensating transactions to undo the impact of previous ones.
A saga is a failure management pattern for long-running distributed transactions.
Why Do We Need Sagas?
Challenges in Distributed Transactions
| Sr.No. | Challenge | Description |
|---|---|---|
| 1 | Lack of global transactions | No XA/2PC (Two Phase Commit) across microservices |
| 2 | Data ownership | Each service owns its data (Database per service) |
| 3 | Partial failures | Some steps may succeed, others may fail |
| 4 | Consistency | Eventual consistency instead of strict ACID |
The Saga pattern helps orchestrate distributed workflows with eventual consistency.
Types of Saga Implementations
Choreography Based Saga
No central controller
Services listen to events and act accordingly
Lightweight, but complex with many services
Example Flow
Order Service → emits OrderCreated
Payment Service → listens, processes payment → emits PaymentCompleted
Inventory Service → reserves stock → emits InventoryReserved
Shipping Service → ships item
If any step fails, a compensating event is triggered.
Orchestration-Based Saga
Central Saga orchestrator directs the flow
Each service executes commands from the orchestrator
Easier to manage, but introduces coupling
Example Flow
Orchestrator → calls Order Service
On success → calls Payment Service
On failure → instructs Order Service to cancel
Real-World Example: E-Commerce Order Processing
Steps
Place Order
Reserve Inventory
Process Payment
Ship Item
Each service has a local database and transaction logic.
If payment fails, we must −
Cancel the order
Release the inventory
This is handled by a Saga.
Saga architecture
Diagram: Choreography Based Saga
Each service publishes and subscribes to events through a broker like Kafka or RabbitMQ.
Implementing Saga Pattern in Spring Boot
Let's implement a Choreography based saga using Spring Boot + Kafka.
Technologies Used
Spring Boot
Spring Kafka
Apache Kafka (as the event broker)
Lombok for model simplification
Maven Dependencies
<dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency>
Example Services and Topics
| Sr.No. | Service | Events Published | Topics Subscribed |
|---|---|---|---|
| 1 | Order Service | OrderCreated, OrderCancelled | PaymentFailed, InventoryFailed |
| 2 | Payment Service | PaymentCompleted, PaymentFailed | OrderCreated |
| 3 | Inventory Service | InventoryReserved, InventoryFailed | PaymentCompleted |
Sample Event: OrderCreatedEvent.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderCreatedEvent {
private String orderId;
private String productId;
private int quantity;
}
Order Service − Kafka Producer
@Service
public class OrderService {
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
public void createOrder(OrderCreatedEvent event) {
kafkaTemplate.send("order-created", event);
}
}
Payment Service − Kafka Consumer
@KafkaListener(topics = "order-created", groupId = "payment-service")
public void handleOrder(OrderCreatedEvent event) {
// Process payment
boolean success = processPayment(event);
if (success) {
kafkaTemplate.send("payment-completed", new PaymentCompletedEvent(event.getOrderId()));
} else {
kafkaTemplate.send("payment-failed", new PaymentFailedEvent(event.getOrderId()));
}
}
Inventory Service − Kafka Consumer
@KafkaListener(topics = "payment-completed", groupId = "inventory-service")
public void handlePayment(PaymentCompletedEvent event) {
// Reserve inventory
boolean success = reserveStock(event.getOrderId());
if (success) {
kafkaTemplate.send("inventory-reserved", new InventoryReservedEvent(event.getOrderId()));
} else {
kafkaTemplate.send("inventory-failed", new InventoryFailedEvent(event.getOrderId()));
}
}
Saga Compensation and Failure Handling
Compensating Transactions
If a step fails (e.g., inventory reservation), previous actions must be reversed−
InventoryFailed → triggers PaymentRollback
PaymentFailed → triggers OrderCancelled
These compensating actions must be idempotent and safe to retry.
Benefits of the Saga Pattern
| Sr.No. | Benefit | Description |
|---|---|---|
| 1 | Decentralized workflow | Maintains autonomy of microservices |
| 2 | Resilience | Can recover from partial failures |
| 3 | Eventual consistency | Instead of strict ACID transactions |
| 4 | Scalable and fault-tolerant | Built on asynchronous messaging |
Challenges and Pitfalls
| Sr.No. | Challenge | Mitigation |
|---|---|---|
| 1 | Complex error handling | Use retries and DLQs |
| 2 | Debugging flows | Use tracing tools like Zipkin |
| 3 | Compensating logic overhead | Modularize and isolate business logic |
| 4 | Message ordering issues | Use Kafka partitions wisely |
Testing a Saga
Approaches
Use Testcontainers to simulate Kafka or RabbitMQ
Verify event flow using integration tests
Mock downstream services using WireMock
Simulate failures to test compensation logic
Real-World Examples
| Sr.No. | Company | Use of Saga Pattern |
|---|---|---|
| 1 | Netflix | Manages distributed workflows in video delivery |
| 2 | Booking.com | Manages hotel bookings, payments, and cancellations |
| 3 | Uber | Handles driver assignment, payments, and cancellations |
| 4 | Amazon | Processes multi-step order and inventory systems |
Best Practices
| Sr.No. | Practice | Reason |
|---|---|---|
| 1 | Use separate event models | Avoid domain model leakage |
| 2 | Make compensating actions idempotent | Safe retries |
| 3 | Implement timeouts | Avoid stuck sagas |
| 4 | Track saga state | Use DB or state store |
| 5 | Use correlation IDs | Easier debugging and tracing |
Conclusion
The Saga pattern provides an elegant solution to the problem of distributed transactions in a microservices architecture. Whether using choreography or orchestration, sagas enable services to maintain data consistency, handle failures gracefully, and ensure resilient workflows.
By combining Spring Boot with Kafka or orchestration engines, developers can build reliable, scalable, and maintainable systems that operate effectively across service boundaries.