Background
현재 사내 시스템 구조개선 진행중에 있습니다. 그중 아래와 같은 요구사항을 가진 시스템을 설계하게 되었습니다.
- A 시스템 영역은 B 시스템 영역에 직접적인 의존성을 가지면 안됨 (ex: API 호출)
- A Component 에서 실행한 로직이 성공하면 결과가 반드시 B Component 전파되어야 함
- A Component 에서 실행에 실패한 로직의 결과가 B Component 로 전파되면 안됨
- A Component 에서 발생한 로직의 결과는 B,C,D Component 로 전파가 될 수도 있음
간략하게 그림으로 표현해봤습니다.
시스템간 소통하는 방법은 다양한 방법이 존재합니다. 위와 같은 요구사항을 만족하기 위해 메시징 방법을 사용하기로 했고, 메시징 플랫폼 중 아파치 카프카를 사용하기로 했습니다. A 시스템에서 메시지를 발행하고 B 또는 다수의 시스템에서 구독을 해야 하기 때문입니다.
하지만 단순히 카프카를 사용해 메시지를 발행, 구독하는것 만으로는 모든 요구사항을 충족시키기 어렵습니다. A 영역에서 발생한 이벤트가 B 영역으로 확실하게 전달이 되어야 함을 보장해야 하고 어떻게 재시도를 할지에 대해서도 고민을 해봐야 합니다. 그리고 A 와 유사하게 발행해야 하는 시스템이 여러개 존재하고, B 와 같이 구독을 해야 하는 시스템이 다수 존재하는데, 모든 컴포넌트에 카프카 의존성을 추가해야 하는게 맞는지도 생각을 해봐야 합니다. 정리하면 아래와 같은 고민사항이 존재합니다.
- 비즈니스 로직과 메시지 발행을 한 트랜잭션에서 처리하는 방법
- 비즈니스 로직이 성공할때만 메시지를 발행함
- 비즈니스 로직 실패시 메시지 발행 X
- 이벤트 순서를 보장하는 방법
- 이벤트 중복처리를 방지하는 방법
- 모든 컴포넌트에 카프카 의존성을 추가하는 것이 맞는지 확인
비즈니스 로직과 메시지 발행을 한 트랜잭션으로 어떻게 처리해야 하는지가 고민을 하다 Transactional Outbox 패턴을 알게 되어 사용하기로 했습니다.
Transactional Outbox?
Outbox 의 뜻을 찾아보면 아래와 같습니다.
Transactional Outbox 는 트랜잭션의 임시 보관함이라고 해석이 됩니다. 이 보관함에는 전송할 메시지나 전송에 실패한 메시지들을 보관됩니다. 임시보관함에 있는 메시지까지 전송을 완료해야 트랜잭션이 완료됩니다. 아래 그림은 Order Service 에서 비즈니스 로직을 수행하고 Order Table 에 데이터를 저장한 후에 메시지를 발행해야 합니다.
Order Serivce 에서 Outbox Table 에 전달해야 하는 이벤트 데이터를 저장하고 Outbox Publisher 에서 Outbox Table 의 데이터를 읽어 Message Broker 로 데이터를 전달합니다. 이렇게 시스템을 구성했을 때 얻을 수 있는 장점은
- Order Table 에 데이터를 넣는 로직과 Outbox Table 에 이벤트를 저장하는 로직을 하나의 트랜잭션으로 묶어 비즈니스 로직과 이벤트 발행을 하나의 단위로 실행할 수 있습니다.
- 이벤트를 Message Broker 로 전달하는 책임은 Outbox Publisher 에서만 담당하게 되니 책임이 분리되어 좀 더 알아보기 쉬운 아키텍처를 만들 수 있을 것 같습니다.
- 모든 DB 가 Outbox Table 의 스키마를 동일하게 가지게 된다면 모든 DB 의 Outbox Publisher 를 단일 소스로 구성할 수 있습니다. 관리해야 할 소스를 줄일 수 있습니다.
- 이벤트 중복 방지 및 순서 보장을 추가 기술 구현 없이 비교적 쉽게 보장받을 수 있습니다.
Outbox Pulisher 에서 이벤트를 읽어 처리하는 로직은 다양한 방법으로 구현할 수 있을 것 같습니다. Outbox Table 을 읽는 로직을 일정 주기마다 실행하는 스케줄러를 개발하거나 CDC 를 통해 Outbox Table 의 Insert 되는 데이터를 감지할 수도 있을 것 같습니다. 그리고 이벤트 중복 처리 방지 및 순서를 보장하는 방법은 Outbox Table 에 Sequential 하면서 고유한 트랜잭션 ID (TSID) 를 부여해 해결할 수도 있을 것 같습니다.
시스템 구조개선을 시작하며 Transactional Outbox Pattern 에 대해 알게 되었고 사용하기 위해 준비중입니다. 현재 시스템 상황 및 방향성과 맞는 패턴이고 앞으로 다양한 영역에서 활용할 수 있는 좋은 패턴이라 생각하여 정리해 보았습니다.