Choreography Saga Pattern
많은 서비스들이 MSA로의 전환을 추진하고 있다.
하지만 모놀리식과는 달리 MSA 환경에서는 트랜잭션 처리가 상당히 어려워진다.
오늘은 MSA에서 트랜잭션을 나이스하게 처리할 수 있도록 지원하는 디자인 패턴 중 하나인 choreography saga pattern에 대해 알아봤다.
https://www.vinsguru.com/choreography-saga-pattern-with-spring-boot/
간단한 트랜잭션
이후 나오게 될 예시는 모두 MSA 환경이라는 것을 전제하고 시작한다.
간단한 비즈니스 디맨드로 사용자가 주문을 생성하는 경우를 생각해보자.
- 주문은 상품의 가격이 사용자의 잔고보다 적거나 같을 때 충족된다.
- 문제의 단순화를 위해 이외의 경우는 생각하지 않는다.
- 주문 서비스와 결제 서비스는 각각의 데이터베이스를 가지고 있다.
- 주문 서비스는 주문 관리에만 책임을 가지고, 결제 서비스는 결제 관리에만 책임을 가진다.
- 서로는 존재를 알고 있지만 행위에 대한 디테일은 모른다.
- 즉 주문 서비스는 어떻게 결제가 이뤄지는지 모르고, 결제 서비스는 주문이 어떻게 이뤄지는지 모른다.
이 방법의 문제는 무엇일까? 세상이 그렇게 쉽지 않다는 것이다.
- 주문 - 결제만 존재한다면 서로가 서로에 대한 요청만 처리하면 된다.
- 하지만 현실의 비즈니스 로직은 이보다 훨씬 복잡한 흐름을 가진다.
- 평범한 주문 생성처럼 상품의 재고를 확인해야 한다는 요구 사항이 들어가게 된다면 어떨까?
- 주문 - 상품 - 결제 간 요청의 흐름을 간단히 처리할 수 없을까?
- 도메인과 도메인끼리는 서로 강결합을 이루고 있다.
- 도메인이 추가될 수록 복잡한 흐름을 트래킹하고 관리하기 어려워진다.
사가 패턴
MSA 환경에서 이뤄지는 비즈니스 트랜잭션을 각 마이크로 서비스에 종속된 로컬 트랜잭션으로 쪼갠다.
이렇게 쪼개진 서비스 로컬 트랜잭션은 Saga
라고 부르고, 비즈니스 워크플로우를 완료하기 위해 순차적으로 실행된다.
사가는 크게 두 가지 방법으로 구현될 수 있다.
이벤트 소싱 기반 사가 패턴
어플리케이션의 상태가 바뀌는 모든 변경점은 이벤트로써 캡쳐된다.
이 이벤트는 데이터베이스 또는 이벤트 저장소에 저장되고, 이벤트 버스를 통해 다른 서비스가 이를 소비할 수 있도록 한다.
주문 서비스는 새 주문을 생성하는 명령을 받는다. 이 요청은 처리된 후에 주문-생성된-이벤트
로써 생성된다.
주문-생성된-이벤트
는 기본적으로 새 주문 요청을 받았으며, 주문 서비스에 의해 PENDING/CREATED 상태로 남게 된다. 이 주문은 아직 수행된 것은 아니다.- 이벤트 객체는 과거형으로 이름 붙여져야 한다. 이미 일어난 일에 대한 데이터를 다루고 있기 때문이다.
이제 결제 서비스나 상품 서비스가 이러한 종류의 이벤트들을 잘 보고 있다가 각 이벤트에 대한 후속 처리를 수행한다.
그리고 이렇게 다른 서비스가 이벤트를 처리하는 것 또한 이벤트로써 취급된다. 주문 서비스는 다른 서비스들이 처리하는 이벤트들을 잘 보고 있다가 주문 요청이 성공했거나 실패했음을 완료 상태로 기록한다.
이러한 접근은 여러 장점이 있다.
- 서비스 간 종속성이 없어진다. 결제 서비스나 상품 서비스는 항상 주문 서비스의 요청을 받을 수 있는 상태가 아니어도 상관 없다.
- 서비스 간 낮은 결합성을 유지할 수 있다.
- 서비스를 수평적으로 확장하기 용이해진다.
- 서비스 간 요청 실패를 처리하기 용이해진다.
이를 Choreography saga pattern이라고 부른다.
사가 패턴 없이는 MSA 환경에서 트랜잭션을 커밋하거나 롤백하는 일은 매우 어렵다. (각 서비스는 데이터 정합성을 위해 이벤트 핸들러를 둬서 트랜잭션을 커밋하거나 롤백하는 이벤트를 계속해서 소비해야 한다.)