Monolith vs MSA 아키텍처 / 오픈소스 생태계 / Transactional Outbox 패턴
한 주마다 뉴스를 적고자 했는데, 이직을 준비하고 직접 하면서 시간이 훌쩍 지나버렸다.
어느 덧 이전 뉴스를 적은지 8개월. 8개월 전하고 지금의 상황과 마음은 참 많이 달라져있다.
기술도 그렇고, 현재 내 상황과 느끼는 부분들도 그렇고.
8개월 전 당시는 지금의 모습이 행복할거라 예상했겠지만(?), 막상 그렇진 않다. 이것도 경험이겠지 하면서 살아가고 있다.
더 많은 도전과 경험, 성장을 위해 뉴스를 다시 작성하기 시작! 4 번째 뉴스 시작!
Monolith냐, MSA냐. 그것이 문제로다. #
(Link) Monoliths are not dinosaurs
요즘 해외에서는 다시 ‘Monolith냐 MSA냐’에 불이 붙는 것 같다.
그 시작은 아마존 프라임 모니터링 서비스를 모놀리스로 다시 재전환하면서 비용을 90%까지 절약할 수 있었다는 글이었다. 여러 AWS Lambda와 AWS Step Function으로 구성되어진 아키텍처를 ECS task로 넘어가며 비용을 줄였다는 이야기이다 (깊게 읽어보진 않음).
- 위 글을 읽고 Monolith Follower들은 ‘봐봐, 역시 Monolith가 더 좋은 경우가 많지.’라고 말하는게 참 보기 안좋다. 뒤에 설명하겠지만, 무조건 적으로 좋은게 어디있겠는가. (MSA를 해보기나 했을까?)
Monolith와 MSA는 언제나 의견차이를 불러오는 주제이다. 내가 생각한 그 이유는 ‘상황에 따라 적합한 것’이 다르기 때문이다.
여러 도메인, 바운디드 컨텍스트가 나뉘고, 관리하는 팀이 분리되며 배포주기가 다른 서비스들이라면 MSA가 적합할 수 있고(이것 또한 무조건 적인 것이 아님), 하나의 팀 서비스면서 서로 연관되어 배포되는 서비스라면 Monolith가 적합할 수 있을 것이다.
중요한 것은 무조건 적인 것은 없다인데, Amazon CTO가 쓴 Monoliths are not dinosaurs
글이 그것을 잘 설명해주고 있다.
사실 프로젝트가 커가면서 위와 같은 상황은 달라질 수 있다. 처음은 Monolith가 적합하다가도, 프로젝트가 커져감에 따라 MSA가 적합할 수 있기 때문.
그렇기에 설계부터 진화 가능한 아키텍처를 생각해야하고(아키텍처가 변경될 것을 대비), 서비스가 성장할 때마다 아키텍처를 재검토하고 다음 단계 성장을 지원할 수 있는지 확인해야한다. 즉, 구조적으로도 리팩토링하는 시간이 필요하다는 것이다.
진화 가능한 아키텍처(Evolvable Architecture)
를 하기 위해서는 어떻게 해야할까? 시스템의 진화를 고려해 최소한의 종속성으로 시스템을 변경하고 확장할 수 있는 기반을 마련하는 것이 좋다.
- 이벤트 중심 아키텍처(EDA), MSA 등이 이러한 사례가 될 수 있겠다.
- Amazon S3가 진화 가능한 아키텍처의 사례라고 한다. 결국 MSA 였는데, 몇 개의 마이크로서비스에서 시작해 현재는 300개 이상의 마이크로서비스로 관리된다고 한다.
하지만 그 과정 속에서 서비스 요구사항이 동일하고, 단일 팀에서 관리하는 서비스라면 결합해 아키텍처를 간소화하는 것이 좋다. 비용적이면에서나 관리측면에서나.
- 개발자가 5명인 스타트업(소규모팀)에서 EDA/MSA가 적합할 수 있을까? 관리할 컴포넌트는 증가하고, 개발은 다 따로 해야하는 것이 번거로움이 될 수 있다는 것.
결론적으로 요약하자면,
- 아키텍처에는 일방통행이 없다.
- 시스템을 정기적으로 평가하고, 상황에 맞게 리팩토링하는 것이 중요하다.
한 팀에, 한 서비스에 오래 있다보면 새로운 것들에 대해 경계심이 생긴다. 이럴 때, 다른 것들을 무시하지말고 현재 자신의 상황에 적합한지도 살펴보고 어떤 게 어떨 때 적합한지도 생각해보고 할 필요가 있다고 생각한다.
오픈소스 개발자를 위한 프로젝트 #
(Link) Something new is brewing
Mac에서 개발 할 때, Brew를 사용해서 패키징 관리를 하곤 한다. 보통 오픈소스 프로젝트/서비스를 사용할 때 기본적으로 활용하는데, 그 이유는 ‘편리함’이 크기 때문이다.
하지만 사용하면서 누가 만들었고, 어떻게 운영되는지는 관심이 별로 없었는데 이 글을 읽으면서 처음 알게되었다.
Brew를 만들었던 Max Howell은 현재는 Brew를 커뮤니티에 맡기고 tea라는 프로젝트를 시작했다. Brew에서 손을 땔 때는 Brew 자체가 훌륭하다고 생각해서 Brew2는 만들지 않을 것이라고 했는데, 어쩌다가 tea 프로젝트를 만들게 되었는지 설명하고 있다.
최근 core-js 이야기도 그렇고 오픈소스 개발자들이 오픈소스로만으로는 삶을 감당할 수 없다고 한다. core-js 같은 경우 정말 많은 프레임워크/서비스의 근간이 되는 오픈소스이지만, 막상 개발자는 경제적으로 홀대받고 있는 것이다.
결국엔 이렇게 되면 해당 오픈소스는 제대로 개발될 수 없게 되고 악영향이 발생할 수 있다(메인테이너는 그냥 자원봉사자가 되는 격). 이러한 문제가 커져 발생할 수 있는 문제를 The Nebraska Problem라고 한다 (실제로 Log4J, NPM 등에서 발생한 문제들이 있다).
Brew에서도 마찬가지였다. Brew 또한 오픈소스였고, 이를 통해 여러 오픈소스를 다운로드/사용할 수 있었기 때문.
Max Howell는 이러한 부정적인 고리를 어떻게 풀어낼까 고민하던 중 블록체인과 Web3에서 이 방법을 찾았고, 오픈소스 유지관리자가 마땅한 보상을 받을 수 있는 서비스인 tea를 개발하게 된 것이다.
사실 블록체인, Web3는 잘 알지 못해서 이와 관련된 내용은 이해하지 못했으나, 블록체인이라는 기술을 통해 긍정적인 영향을 만들어낼 수 있다는 것은 참 좋은 아이디어 같다.
개발자의 삶 속에서는 오픈소스를 빼놓지 못할텐데 사실 그 개발자(메인테이너)들에게 합당한 보상을 해주고 있는가? 라는 궁금증과 오픈소스인데 보상이 필요한가?라는 이기적인 생각도 들기도 한다.
어찌됬든, tea 같은 프로젝트를 통해 사용자들로 하여금 보상을 받으면 오픈소스 생태계가 더 활성화 될 것 이라는 건 자명한 것 같다. 이런 프로젝트들이 많이 나왔으면 좋겠다.
Transactional outboxing #
(Link) Transactional outboxing with Postgres push notifications
위 글에서는 Transactional Outbox pattern을 PostgreSQL과 Spring boot 3.x에서 구현하는 법에 대해 설명한다.
먼저 간단하게 Transactional Outbox pattern을 설명하고 있는데 간단히 설명하면 DB 변경과 이벤트 발행(Publishing)을 하나의 Transaction(트랜잭션) 안에서 동작하기 위한 방법이라고 볼 수 있다.
- 이는 MSA 구조의 DDD 같은 곳에서 도메인 이벤트를 발생하여 다른 컴포넌트에서 해당 이벤트로 하여금 트리거시키고 싶은 경우 등에 활용할 수 있다.
그냥 @Trasnactional
걸고, DB 데이터 넣으면서 이벤트 발생시키면 안돼? 라고 할 수 있지만, Message broker에는 Database transaction을 함께 사용하지 못한다는 문제가 있다.
데이터 변경은 성공했지만 이벤트 발행은 실패하거나, 반대로 이벤트 발행은 성공했으나 데이터 변경은 실패하는 경우가 발생할 수 있다. 즉, 데이터의 일관성이 깨질 수 있다는 것.
- 이러한 문제를 해결하기 위한 방법으로 Transaction Outbox pattern이 사용된다. MQ의 역할로서 Database table을 이용하자는 것이다.
- Message broker에 이벤트를 발행하는 대신
OUTBOX
DB table에 메시지 관련 데이터들을 저장한다. 별도의 Message Relay가OUTBOX
table을 폴링하면서 이벤트 데이터를 읽어 Message broker로 발행한다. - 얼마나 자주 폴링하는가에 따라 데이터 변경과 이벤트 발행의 시차가 발생할 수는 있지만, 결과적으로 일관성(Eventual Consistency)은 유지할 수 있다. (참조: MSA에서 메시징 트랜잭션 처리하기)
PostgreSQL에서는 LISTEN
, NOTIFY
매커니즘을 통해 데이터베이스 연결 전반에 비동기적으로 메시지를 전달할 수 있는 기능을 제공한다.
Spring integration에서 JdbcChannelMessageStore
, PostgresChannelMessageTableSubsriber
를 통해 이 푸시알림들을 받을 수 있도록 지원하는 것 같다.
이를 통해 번거롭게 특정 테이블을 폴링하는 로직을 작성하지 않아도, 손쉽게 Listener(Message Relay)를 구현하고, Transactional Outbox Pattern을 구현할 수 있다.