Enterprise Architecture · Enterprise
Saga Choreography with Kafka
Choreography-based Saga uses domain events to coordinate multi-service transactions without a central orchestrator. Each service reacts to events and emits its own.
// === ORDER SERVICE ===
@Service
public class OrderService {
@Transactional
public void createOrder(CreateOrderRequest req) {
Order order = orderRepo.save(Order.pending(req));
// Triggers SAGA: PaymentService listens to this
kafkaTemplate.send("orders.order.created",
order.getId(), new OrderCreatedEvent(order));
}
// Compensating action if payment fails
@KafkaListener(topics = "payments.payment.failed")
@Transactional
public void onPaymentFailed(PaymentFailedEvent event) {
Order order = orderRepo.findById(event.getOrderId()).orElseThrow();
order.cancel("Payment failed: " + event.getReason());
orderRepo.save(order);
// Saga compensation complete for order service
kafkaTemplate.send("orders.order.cancelled", order.getId(), new OrderCancelledEvent(order));
}
@KafkaListener(topics = "payments.payment.completed")
@Transactional
public void onPaymentCompleted(PaymentCompletedEvent event) {
Order order = orderRepo.findById(event.getOrderId()).orElseThrow();
order.confirmPayment();
orderRepo.save(order);
// Triggers next saga step: Inventory reservation
kafkaTemplate.send("inventory.reservation.requested",
order.getId(), new ReservationRequest(order));
}
}
// === PAYMENT SERVICE ===
@Service
public class PaymentService {
@KafkaListener(topics = "orders.order.created")
@RetryableTopic(attempts = "3", backoff = @Backoff(delay = 2000))
@Transactional
public void onOrderCreated(OrderCreatedEvent event) {
try {
PaymentResult result = paymentGateway.charge(event.getAmount(), event.getCustomerId());
kafkaTemplate.send("payments.payment.completed",
event.getOrderId(), new PaymentCompletedEvent(event, result));
} catch (PaymentException e) {
kafkaTemplate.send("payments.payment.failed",
event.getOrderId(), new PaymentFailedEvent(event, e.getMessage()));
}
}
}