Skip to content

사가 편성

사가는 단계를 편성하는 로직으로 구성된다. 시스템 커맨드가 사가를 시작할 때 해당 편성 로직은 첫 번째 사가 참여자를 정하여 로컬 트랜잭션 실행을 지시하고, 트랜잭션이 완료되면 모든 단계를 실행 될때까지 계속 그다음 사가 참여자를 호출한다. 로컬 트랜잭션이 도중에 하나라도 실패한다면 사가는 보상 트랜잭션을 역순으로 실행한다. 이와 같은 사가를 편성하는 로직은 두 가지 종류가 있다.

종류설명
코레오그래피(choreography)의사 결정과 순서화를 사가 참여자에게 맡긴다. 사가 참여자는 주로 이벤트 교환 방식으로 통신한다.
오케스트레이션(orchestration)사가 편성 로직을 사가 오케스트레이터에 중앙화한다. 사가 오케스트레이터는 사가 참여자에게 커맨드 메시지를 보내 수행할 작업을 지시한다.

코레오그래피 사가

코레오그래피 방식은 중앙 편성자 없이, 서로 이벤트를 구독하여 그에 따라 반응하는 것이다. 아래 그림을 코레오그래피 스타일로 설계한 주문 생성 사가이다. 사가 참여자는 서로 이벤트를 주고 받으며 소통한다. 주문 서비스를 시작으로 각 참여자는 자신의 DB를 업데이트하고 다음 참여자를 트리거하는 이벤트를 발행한다.

사용자가 주문 요청을 보낸 경우, 이벤트 순서는 다음과 같다.

  1. 주문 서비스: 주문을 APPROVAL_PENDING 상태로 생성 → 주문 생성 이벤트 발행
  2. 소비자 서비스: 주문 생성 이벤트 수신 → 소비자가 주문을 할 수 있는지 확인 → 소비자 확인 이벤트 발행
  3. 주방 서비스: 주문 생성 이벤트 수신 → 주문 내역 확인 → 티켓을 CREATE_PENDING 상태로 생성 → 티켓 생성됨 이벤트 발행
  4. 회계 서비스: 주문 생성 이벤트 수신 → 신용카드 승인 PENDING 상태로 생성
  5. 회계 서비스: 티켓 생성 및 소비자 확인 이벤트 수신 → 소비자 신용카드 과금 → 신용카드 승인됨 이벤트 발행
  6. 주방 서비스: 신용카드 승인 이벤트 수신 → 티켓 상태 AWAITING_ACCEPTANCE로 변경
  7. 주문 서비스: 신용카드 승인됨 이벤트 수신 → 주문 상태 APPROVED로 변경 → 주문 승인됨 이벤트 발행

회계 서비스에서 신용카드가 승인 거부된 경우, 이벤트 순서는 다음과 같다.

  1. 주문 서비스: 주문을 APPROVAL_PENDING 상태로 생성 → 주문 생성 이벤트 발행
  2. 소비자 서비스: 주문 생성 이벤트 수신 → 소비자가 주문을 할 수 있는지 확인 → 소비자 확인 이벤트 발행
  3. 주방 서비스: 주문 생성 이벤트 수신 → 주문 내역 확인 → 티켓 상태 CREATE_PENDING으로 생성 → 티켓 생성 이벤트 발행
  4. 회계 서비스: 주문 생성 이벤트 수신 → 신용카드 승인 PENDING 상태로 생성
  5. 회계 서비스: 티켓 생성 및 소비자 확인 이벤트 수신 → 소비자 신용카드 과금 → 신용카드 승인 실패 이벤트 발행
  6. 주방 서비스: 신용카드 승인 실패 이벤트 수신 → 티켓 상태 REJECTED로 변경
  7. 주문 서비스: 신용카드 승인 실패 이벤트 수신 → 주문 상태 REJECTED로 변경

코레오그래피 방식으로 사가를 구현하려면 두가지 통신 이슈를 고려해야한다.

첫째, 사가 참여자가 자신의 DB를 업데이트한 뒤에, DB 트랜잭션의 일부로 이벤트를 발행하도록 해야한다. 다시말해, 로컬 트랜잭션의 실행이 성공하였을 시 이벤트를 발행하여 전달한다는 것을 무조건! 보장해야한다. 따라서 사가 참여자가 서로 확실하게 통신하려면 트랜잭셔널 메시징(트랜잭션 로그 테일링 패턴과 같은…)을 사용해야한다.

둘째, 사가 참여자는 자신이 수신한 이벤트와 자신이 가진 데이터를 연관지을 수 있어야 한다. 데이터를 매핑할 수 있는 상관관계 ID를 포함하여 이벤트를 발행함으로써 해결할 수 있다.

코레오그래피 사가의 장점

  • 단순함: 비즈니스 객체를 생성, 수정, 삭제할 때 서비스가 이벤트를 발행하기만 하면 된다.
  • 느슨한 결합: 참여자는 이벤트를 구독할 뿐, 서로를 직접 알지 못한다.

코레오그래피 사가의 단점

  • 복잡함: 오케스트레이션 사가와 달리, 사가를 어느 한골에 정의한 것이 아니라서 여러 서비스에 구현 로직이 흩어져있다. 그렇기 때문에 어떤 로직이 어떻게 작동하는지 이해하기가 어렵다.
  • 순환의존성: 참여자가 서로 이벤트를 구동하는 특성상 순환 의존성(cyclic dependency)이 발생하기 쉽다. 꼭 문제라고 할 순 없지만, 잠재적인 설계 취약점이 될 수 있다.
  • 단단히 결합될 가능성: 사가 참여자는 각자 자신에게 영향을 미치는 이벤트를 모두 구독해야한다. 예를 들어 회계 서비스는 신용카드를 과금/환불 처리하게 만드는 모든 이벤트에 호출되어야 하기 때문에, 다른 서비스에 변동 사항이 생길때마다 항상 같이 수정해야만 하는 강결합이 생길 수 있다.

오케스트레이션 사가

오케스트레이션 사가에서는 사가 참여자가 할 일을 알려주는 오케스트레이터 클래스를 정의한다. 사가 오케스트레이터는 커맨드/비동기 응답 상호작용을 하며 참여자와 통신한다. 사가 참여자가 작업을 마치고 응답 메시지를 오케스트레이터에 주면, 오케스트레이터는 응답 메시지를 처리한 후 다음 사가 단계를 어느 참여자가 수행할지 결정한다.

아래 그림은 오케스트레이션 스타일로 설계한 주문 생성 사가이다. 주문 서비스에 구현된 사가 오케스트레이터는 비동기 요청/응답을 통해 사가 참여자를 호출하고 그 처리 과정에 따라 커맨드 메시지를 전송한다. 그리고 자신의 응답 채널에서 메시지를 읽어 다음 사가 단계를 결정한다.

주문 생성 요청이 들어왔을때 처리 흐름은 다음과 같다.

  1. 사가 오케스트레이터가 소비자 서비스에 소비자 확인 커맨드 전송
  2. 소비자 서비스가 소비자 확인 메시지를 응답
  3. 사가 오케스트레이터는 주방 서비스에 티켓 생성 커맨드 전송
  4. 주방 서비스가 티켓 생성 메시지 응답
  5. 사가 오케스트레이터가 회계 서비스에 신용카드 승인 메시지 전송
  6. 회계 서비스가 신용카드 승인됨 메시지 응답
  7. 사가 오케스트레이터가 주방 서비스에 티켓 승인 커맨드 전송
  8. 사가 오케스트레이터가 주문 서비스에 주문 승인 커맨드 전송

제일 마지막 단계에서 사가 오케스트레이터는 커멘드 메시지를 주문 서비스에 전송한다. 물론 주문 생성 사가가 주문 서비스의 한 컴포넌트이기 때문에 주문을 직접 업데이트해서 승인 처리해도 되지만, 일관성 차원에서 마치 다른 참여자인 것 처럼 취급하는 것이다.

오케스트레이션 사가에서 실패하는 케이스를 처리하기 위해서는 상태 기계를 모델링하여 유용하게 사용할 수 있다. 상태 기계(Finite-State Machine)는 상태와 이벤트에 의해 트리거되는 상태 전이(transition)로 구성된다. 전이가 발생할때마다 참여자를 호출하고, 상태간 전이는 참여자가 로컨 트랜잭션을 완료하는 시점에 트리거하면 된다. 상태 기계를 이용하여 사가를 모델링하면 설계, 구현, 테스트를 더 쉽게 진행할 수 있다.

주문 생성 사가를 상태 기계로 표현하면 아래 그림과 같이 나타낼 수 있다.

상태 기계는 사가 참여자의 여러가지 응답에 따라 다양한 상태 전이를 거치면서 결국 주문 승인, 또는 거부 두 상태중 한쪽으로 귀결된다.


오케스트레이션 사가의 장점

  • 의존관계 단순화: 오케스트레이터는 참여자를 호출하지만 참여자는 로케스트레이터를 호출하지 않으므로 순환 의존성이 발생하지 않는다.
  • 느슨한 결합: 각 서비스는 오케스트레이터가 호출하는 API를 구현할 뿐, 사가 참여자가 발행하는 이벤트는 몰라도 된다.
  • *관심사 분리, 로직 단순화: 사가 편성 로직이 사가 오케스트레이터 한곳에만 있으므로 도메인 객체는 더 단순해지고 자신이 참여한 사가에 대해서는 알지 못한다.

오케스트레이션 사가의 단점

  • 병목현상: 여러 서버에서 메시지를 받아 다음 참여자로 요청을 보내는 동작을 모두 한 곳에서 수행해야 하기 때문에, 아무리 비동기적으로 대기 없이 처리하더라도 트래픽이 한 곳으로 몰려 병목현상이 일어날 수 있다. (위 예시와 다르게, 비즈니스 로직을 가지지 않고 오직 순서화만 담당하는 오케스트레이터를 분리해내면 일부 해결되긴 한다.)

참고

마이크로서비스패턴

https://microservices.io/patterns/data/saga.html