MSA 환경에서 분산 트랜잭션(Distributed Transaction)을 2PC 없이 해결하기 위한 패턴을 말한다.
즉, 여러 서비스가 하나의 비즈니스 로직을 처리할 때 전체를 하나의 일관된 트랜잭션처럼 보이게 만드는 구조를 말한다.
다만, RDB 내 로컬 트랜잭션과는 다르게 글로벌 락 없이, 각 서비스가 독립적으로 로컬 트랜잭션을 수행한다.
Saga는 이를 위해 정상 흐름(Forward Flow)과
보상 트랜잭션(Compensation Transaction)을 정의한다.
보상 트랜잭션은 반드시 비즈니스적으로 롤백 동작이 존재해야 하고,
데이터베이스 undo가 아니라 “도메인 undo 작업”이다.
각 서비스가 이벤트를 보고 다음 행동을 스스로 결정하는 방식.
▶ 이벤트 기반 아키텍처 중심
중앙 Orchestrator가 전체 Saga의 흐름을 지휘.
Orchestrator → Payment → Inventory → Shipping
Orchestrator가 직접 보상 요청
Shipping 실패 → Orchestrator가 Inventory 보상 → Payment 보상 → Order 보상
| 문제 | Saga 해결 방식 |
|---|---|
| 분산된 DB 간 트랜잭션 일관성 | 보상 트랜잭션으로 논리적 Rollback |
| 서비스 장애 전파 위험 | 느슨한 결합(Choreography) 또는 중앙 제어(Orchestration) |
| 2PC 성능 저하 | 로컬 트랜잭션만 사용 |
| Eventually Consistency | 비동기 이벤트 기반으로 점진적 일관성 확보 |
→ “Eventually Consistent”만 보장
→ 보상 트랜잭션이 실패할 수도 있음
→ “보상 실패 처리 전략”까지 고려해야 한다
// OrderCreatedEvent 발행
orderEventProducer.send(new OrderCreatedEvent(orderId));
// PaymentConsumer
@KafkaListener(topics = "order-created")
public void handle(OrderCreatedEvent event) {
paymentService.pay(event.orderId());
}
public void startOrderSaga(UUID orderId) {
try {
paymentClient.pay(orderId);
inventoryClient.deduct(orderId);
shippingClient.create(orderId);
} catch (Exception e) {
compensate(orderId);
}
}
private void compensate(UUID orderId) {
shippingClient.cancel(orderId);
inventoryClient.restore(orderId);
paymentClient.refund(orderId);
}
규모 있는 MSA에서는 대부분
대규모 전자상거래(예: 쿠팡, 아마존) 같은 곳은
배송/결제/재고 조합이 매우 복잡하기 때문에 오케스트레이터 서비스가 거의 필수.