Files
gh-giuseppe-trisciuoglio-de…/skills/spring-boot/spring-boot-saga-pattern/references/02-choreography-implementation.md
2025-11-29 18:28:30 +08:00

3.9 KiB

Choreography-Based Saga Implementation

Architecture Overview

In choreography-based sagas, each service produces and listens to events. Services know what to do when they receive an event. No central coordinator manages the flow.

Service A → Event → Service B → Event → Service C
    ↓                   ↓                   ↓
  Event              Event               Event
    ↓                   ↓                   ↓
Compensation    Compensation        Compensation

Event Flow

Success Path

  1. Order Service creates order → publishes OrderCreated event
  2. Payment Service listens → processes payment → publishes PaymentProcessed event
  3. Inventory Service listens → reserves inventory → publishes InventoryReserved event
  4. Shipment Service listens → prepares shipment → publishes ShipmentPrepared event

Failure Path (When Payment Fails)

  1. Payment Service publishes PaymentFailed event
  2. Order Service listens → cancels order → publishes OrderCancelled event
  3. All other services respond to cancellation with cleanup

Event Publisher

@Component
public class OrderEventPublisher {
    private final StreamBridge streamBridge;

    public OrderEventPublisher(StreamBridge streamBridge) {
        this.streamBridge = streamBridge;
    }

    public void publishOrderCreatedEvent(String orderId, BigDecimal amount, String itemId) {
        OrderCreatedEvent event = new OrderCreatedEvent(orderId, amount, itemId);
        streamBridge.send("orderCreated-out-0",
            MessageBuilder
                .withPayload(event)
                .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
                .build());
    }
}

Event Listener

@Component
public class PaymentEventListener {

    @Bean
    public Consumer<OrderCreatedEvent> handleOrderCreatedEvent() {
        return event -> processPayment(event.getOrderId());
    }

    private void processPayment(String orderId) {
        // Payment processing logic
    }
}

Event Classes

public record OrderCreatedEvent(
    String orderId,
    BigDecimal amount,
    String itemId
) {}

public record PaymentProcessedEvent(
    String paymentId,
    String orderId,
    String itemId
) {}

public record PaymentFailedEvent(
    String paymentId,
    String orderId,
    String itemId,
    String reason
) {}

Spring Cloud Stream Configuration

spring:
  cloud:
    stream:
      bindings:
        orderCreated-out-0:
          destination: order-events
        paymentProcessed-out-0:
          destination: payment-events
        paymentFailed-out-0:
          destination: payment-events
      kafka:
        binder:
          brokers: localhost:9092

Maven Dependencies

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>

Gradle Dependencies

implementation 'org.springframework.cloud:spring-cloud-stream'
implementation 'org.springframework.cloud:spring-cloud-stream-binder-kafka'

Advantages and Disadvantages

Advantages

  • Simple for small number of services
  • Loose coupling between services
  • No single point of failure
  • Each service is independently deployable

Disadvantages

  • Difficult to track workflow state - distributed across services
  • Hard to troubleshoot - following event flow is complex
  • Complexity grows with number of services
  • Distributed source of truth - saga state not centralized

When to Use Choreography

Use choreography-based sagas when:

  • Microservices are few in number (< 5 services per saga)
  • Loose coupling is critical
  • Team is experienced with event-driven architecture
  • System can handle eventual consistency
  • Workflow doesn't need centralized monitoring