DDD비동기 보상 트랜잭션
| BC | AR | Aggregates | 책임 |
|---|---|---|---|
| Catalog | Product, Variant | — | 상품·옵션 정보 (현 시점 정보) |
| Inventory | Stock | Reservation | 재고 차감·예약 (동시성 제어) |
| Order | Order | OrderItem, OrderAmount | 주문 진행·취소 (주문 시점 정보) |
| Payment | Payment | Transaction | 결제 승인·취소 (외부 PG사 연동) |
결제는 주문번호와 금액만 알면 됨
결제는 외부 API (PG사)와 연동
→ API 연동 장애를 고려해, 주문 서비스와 격리 필요
PG사 API를 호출하기 때문에, 결국 분산 트랜잭션 처리가 필요
BC별로 별개의 DB를 사용하기 때문에, SAGA 패턴을 선택했습니다
Event-driven ochestration 방식을 채택했습니다.
가능하면 비동기로 처리하기 위함
Choreography 방식은 연쇄 트리거 느낌
→ 사가 흐름 추적이 어렵기 때문에, 금전적인 도메인 특성상 배제
주문 SAGA 프로세스


주문 SAGA의 보상은 “결제”를 기준으로 되도록 구현했습니다.
SAGA 순서 (보상 필요한 단계들만)
| SAGA 시작 |
|---|
| 재고 예약 |
| 주문 생성 |
| 결제 승인 |
| 재고 차감 |
| 주문 확정 |
| SAGA 종료 |
실제 보상 로직
| SAGA 상태 → 보상 중 |
|---|
| 결제 취소 시도 |
| 재고 복구 or 예약 해제 |
| 주문 실패 처리 |
| SAGA 상태 → 보상 성공 |
보상 프로세스는 3가지가 지켜져야 합니다.
가까운 시점에 반드시 실행되어야 함
→ 스케쥴러나 이벤트로 감지 필요
실패 시 재시도, 일정 횟수 이상 실패하면 수동 처리가 되어야 함
→ 보상도 분산 트랜잭션이므로, 중간에 장애 시 대처 필요
각 단계는 멱등해야 함
→ 재시도 시에 중복 요청이 발생할 수 있기 때문
처음엔 데이터를 한 번에 긁어 처리하는 Chunk 방식의 Batch를 떠올렸지만, 구체화할수록 의문이 생겼습니다.
보상 프로세스는 Saga 실패 직후가 아닌, 별도 스케쥴러가 주기적으로 감지해 실행하는 것으로 설계했습니다.
재시도 가능성이 있기 때문에, 실패 시점 불분명
→ TTL을 넘으면 실패로 간주
TTL이 있기 때문에, 주기적 감지 필요
결론적으로 스레드 풀을 가진 스케쥴러 워커를 구현했습니다.