Saga Pattern in Microservices: A Step-by-Step Guide

Introduction

In a distributed microservices architecture, ensuring data consistency across multiple services is one of the biggest challenges. The Saga Pattern is a design pattern used to manage distributed transactions in microservices, ensuring eventual consistency across different services.

This guide will break down the Saga Pattern in a simple and easy-to-understand way, using real-world examples and a fully working implementation with Spring Boot, Kafka, MySQL, and compensation logic.

1️⃣ Understanding the Saga Pattern

When a business transaction spans multiple microservices, we need a way to ensure that all steps either complete successfully or rollback changes if something goes wrong. Instead of using traditional database transactions (ACID properties), which are hard to manage across services, we use the Saga Pattern, where each service performs its part and notifies the next service.

📌 Key Idea:

  • Break a transaction into small independent steps.

  • If any step fails, execute compensating actions to undo the previous steps.

  • Ensures eventual consistency instead of strong consistency.

Types of Saga Implementation

1. Choreography-Based Saga (Event-Driven Approach)

  • Each service publishes an event after it completes its work.

  • Other services listen for these events and trigger the next step.

  • No centralized controller.

2. Orchestration-Based Saga (Centralized Control)

  • A Saga Orchestrator is responsible for managing all steps.

  • It ensures that each step executes in sequence and handles rollbacks.

2️⃣ Real-World Example: E-commerce Order Processing 🛒

Scenario:

A customer places an order, and the system processes it through the following microservices: 

1️⃣ Order Service – Creates the order. 

2️⃣ Payment Service – Processes the payment. 

3️⃣ Inventory Service – Reserves stock. 

4️⃣ Shipping Service – Ships the product.

Handling Failures:

  • If payment fails, the order should be canceled.
  • If inventory is out of stock, the payment should be refunded.
  • If shipping fails, the inventory should be restocked and the payment refunded.

This is where the Saga Pattern ensures a smooth transaction flow with automatic rollback mechanisms.

Saga Pattern in Microservices: A Step-by-Step Guide

3️⃣ Step-by-Step Implementation Using Spring Boot

Step 1: Define the Shared Order Event Model

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderEvent {
    private Long orderId;
    private String status; // PENDING, COMPLETED, CANCELLED
    private String service; // ORDER, PAYMENT, INVENTORY, SHIPPING
}

Step 2: Configure Kafka Topics in application.yml

spring:
  kafka:
    bootstrap-servers: localhost:9092
    consumer:
      group-id: saga-group
      auto-offset-reset: earliest
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer

Step 3: Implement Order Service (8081)

@RestController
@RequestMapping("/orders")
public class OrderController {
    @Autowired private KafkaTemplate<String, OrderEvent> kafkaTemplate;
    @Autowired private OrderRepository orderRepository;
    
    @PostMapping("/create")
    public ResponseEntity<String> createOrder(@RequestBody Order order) {
        order.setStatus("PENDING");
        orderRepository.save(order);
        kafkaTemplate.send("order-topic", new OrderEvent(order.getId(), "PENDING", "ORDER"));
        return ResponseEntity.ok("Order Created Successfully");
    }
}

Step 4: Implement Payment Service (8082)

@Service
public class PaymentService {
    @KafkaListener(topics = "payment-topic", groupId = "saga-group")
    public void processPayment(OrderEvent event) {
        if (new Random().nextBoolean()) { // Simulate failure
            kafkaTemplate.send("rollback-topic", new OrderEvent(event.getOrderId(), "CANCELLED", "PAYMENT"));
        } else {
            kafkaTemplate.send("inventory-topic", new OrderEvent(event.getOrderId(), "PENDING", "INVENTORY"));
        }
    }
}

Step 5: Implement Inventory Service (8083)

@Service
public class InventoryService {
    @KafkaListener(topics = "inventory-topic", groupId = "saga-group")
    public void reserveStock(OrderEvent event) {
        if (new Random().nextBoolean()) { // Simulate failure
            kafkaTemplate.send("rollback-topic", new OrderEvent(event.getOrderId(), "CANCELLED", "INVENTORY"));
        } else {
            kafkaTemplate.send("shipping-topic", new OrderEvent(event.getOrderId(), "PENDING", "SHIPPING"));
        }
    }
}

Step 6: Implement Shipping Service (8084)

@Service
public class ShippingService {
    @KafkaListener(topics = "shipping-topic", groupId = "saga-group")
    public void shipProduct(OrderEvent event) {
        if (new Random().nextBoolean()) { // Simulate failure
            kafkaTemplate.send("rollback-topic", new OrderEvent(event.getOrderId(), "CANCELLED", "SHIPPING"));
        } else {
            kafkaTemplate.send("saga-complete", new OrderEvent(event.getOrderId(), "COMPLETED", "SHIPPING"));
        }
    }
}

Step 7: Implement Rollback Mechanism

@Service
public class RollbackService {
    @KafkaListener(topics = "rollback-topic", groupId = "saga-group")
    public void rollbackTransaction(OrderEvent event) {
        System.out.println("Rolling back transaction: " + event);
    }
}

4️⃣ Best Practices for Implementing the Saga Pattern

Use Event-Driven Architecture – Kafka, RabbitMQ, or AWS SQS for event-based messaging. 

Ensure Idempotency – Avoid processing the same event multiple times. 

Implement Logging & Monitoring – Use tools like Zipkin, Jaeger, or Prometheus

Automate Error Handling & Retries – Ensure compensating transactions run smoothly. 

Test Failure Scenarios – Simulate real-world failures to ensure rollback logic works.


🎯 Conclusion

The Saga Pattern provides a robust way to manage distributed transactions in microservices, ensuring data consistency and fault tolerance. By implementing the Orchestration-Based Saga Pattern with Spring Boot, Kafka, and MySQL, we ensure that transactions either complete successfully or rollback without inconsistencies.

🚀 Key Takeaways:

✔ Ensures eventual consistency across microservices. 

✔ Two types: Choreography (event-driven) and Orchestration (centralized control)

✔ Implemented using Spring Boot, Kafka, and event-driven messaging.

Implementing the Saga Pattern enhances scalability, fault tolerance, and resilience in microservices-based architectures! 🚀

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare