3.9 KiB
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
- Order Service creates order → publishes
OrderCreatedevent - Payment Service listens → processes payment → publishes
PaymentProcessedevent - Inventory Service listens → reserves inventory → publishes
InventoryReservedevent - Shipment Service listens → prepares shipment → publishes
ShipmentPreparedevent
Failure Path (When Payment Fails)
- Payment Service publishes
PaymentFailedevent - Order Service listens → cancels order → publishes
OrderCancelledevent - 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