Saga 패턴

1. Orchestration (중앙 관리)

            [Saga Orchestrator]
                 |
    ┌─────────────┼─────────────┐
    ↓             ↓             ↓
[주문서비스]   [결제서비스]   [재고서비스]   [배송서비스]

실제 동작 과정

// Saga Orchestrator 의사코드
public class OrderSaga {
    
    public void processOrder(OrderRequest request) {
        try {
            // 1단계: 주문 생성
            Order order = orderService.createOrder(request);
            
            // 2단계: 결제 처리
            Payment payment = paymentService.processPayment(order);
            
            // 3단계: 재고 차감
            Stock stock = stockService.reduceStock(order);
            
            // 4단계: 배송 요청
            Shipping shipping = shippingService.requestShipping(order);
            
            // 모든 단계 성공!
            
        } catch (PaymentFailedException e) {
            // 결제 실패 시: 주문만 취소
            orderService.cancelOrder(order.getId());
            
        } catch (StockInsufficientException e) {
            // 재고 부족 시: 결제 취소 → 주문 취소
            paymentService.refund(payment.getId());
            orderService.cancelOrder(order.getId());
            
        } catch (ShippingFailedException e) {
            // 배송 실패 시: 재고 복구 → 결제 취소 → 주문 취소
            stockService.restoreStock(stock.getId());
            paymentService.refund(payment.getId());
            orderService.cancelOrder(order.getId());
        }
    }
}

2. Compensating Transaction (보상 작업)

보상 작업이란? 이미 성공한 작업을 “되돌리는 작업”

실제로는 데이터를 삭제하지 않고, 상태를 변경하는 방식

// 주문 서비스
class OrderService {
    // 정상 작업
    public Order createOrder(OrderRequest request) {
        Order order = new Order(request);
        order.setStatus("CREATED");  // 상태: 생성됨
        return orderRepository.save(order);
    }
    
    // 보상 작업
    public void cancelOrder(Long orderId) {
        Order order = orderRepository.findById(orderId);
        order.setStatus("CANCELLED");  // 상태만 변경 (삭제 X)
        orderRepository.save(order);
    }
}

// 결제 서비스  
class PaymentService {
    // 정상 작업
    public Payment processPayment(Order order) {
        Payment payment = new Payment(order);
        payment.setStatus("COMPLETED");  // 결제 완료
        // 실제 카드사 API 호출
        return paymentRepository.save(payment);
    }
    
    // 보상 작업
    public void refund(Long paymentId) {
        Payment payment = paymentRepository.findById(paymentId);
        payment.setStatus("REFUNDED");  // 환불 상태로 변경
        // 실제 환불 API 호출
        paymentRepository.save(payment);
    }
}

// 재고 서비스
class StockService {
    // 정상 작업
    public Stock reduceStock(Order order) {
        Stock stock = stockRepository.findByProductId(order.getProductId());
        stock.setQuantity(stock.getQuantity() - order.getQuantity());
        return stockRepository.save(stock);
    }
    
    // 보상 작업
    public void restoreStock(Long stockId) {
        Stock stock = stockRepository.findById(stockId);
        stock.setQuantity(stock.getQuantity() + order.getQuantity());  // 수량 복구
        stockRepository.save(stock);
    }
}

3. Choreography 방식 (이벤트 기반)

주문서비스 --OrderCreated--> 결제서비스 --PaymentCompleted--> 재고서비스
    ↑                                                              |
    └--------StockReduced 이벤트 ← 배송서비스 ←--StockReduced------┘
// 주문 서비스
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
    // 결제 서비스에 이벤트 발행
    eventPublisher.publish(new PaymentRequestEvent(event.getOrderId()));
}

// 결제 서비스  
@EventListener
public void onPaymentRequest(PaymentRequestEvent event) {
    try {
        processPayment(event.getOrderId());
        // 성공 시 다음 단계로
        eventPublisher.publish(new PaymentCompletedEvent(event.getOrderId()));
    } catch (Exception e) {
        // 실패 시 보상 이벤트
        eventPublisher.publish(new PaymentFailedEvent(event.getOrderId()));
    }
}

// 주문 서비스가 실패 이벤트 수신
@EventListener  
public void onPaymentFailed(PaymentFailedEvent event) {
    cancelOrder(event.getOrderId());  // 보상 작업 실행
}

핵심 정리 Saga 패턴 = 각자 일하고, 실패하면 이전 일들을 차례로 되돌리기

정상 흐름: A → B → C → D (순차 실행) 실패 시: C에서 실패 → B 되돌리기 → A 되돌리기

보상 작업의 핵심:

실제 삭제가 아닌 상태 변경 순서가 중요 (역순으로 되돌려야 함) 각 보상 작업은 실패하면 안 됨 (멱등성 보장)