본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
강의 요약
오늘 강의에서는 Event-Driven Architecture의 개념과 마틴 파울러가 정의한 네 가지 핵심 패턴을 다루었다. Event Notification, Event-Carried State Transfer, Event Sourcing, CQRS에 대해 알아보고, 그 전에 event와 command와 대해서도 먼저 알아보자.
Event, Command and Message
EDA를 이해하기 위해서는 먼저 세 가지 핵심 개념을 구분해야 한다.
Message
- 한 시스템에서 다른 시스템으로 전송하는 요청으로, 처리에 필요한 모든 페이로드를 포함한다.
- Message 자체는 특별한 의도나 의미를 담지 않는 일반적인 개념이다.
Event
- 과거에 발생한 사건을 알리기 위한 메시지다.
- 이미 발생한 사실을 나타내므로 불변이며, 이벤트를 승인하거나 거부하는 것은 불가능하다.
- 이벤트 이름은 일반적으로 과거형으로 표현된.
- eg) ProductPurchased, PaymentFinished
Command
- 특정 행동을 수행하도록 요청하는 메시지다.
- 미래에 일어날 가능성을 나타내므로 수신 시스템이 이를 승인하거나 거부할 수 있다.
- 커맨드는 일반적으로 동사 형태로 표현된다
- eg) SendEmail, ExecutePayment
Event와 Command의 소유권
- Event의 소유권은 발행자(Producer)에게 있다.
- 이벤트는 발행자의 도메인에서 발생한 사실이므로, 발행자가 이벤트의 형태와 내용을 결정한다.
- 반면 Command의 소유권은 소비자(Consumer)에게 있다.
- 여러 발행자가 동일한 커맨드를 전송할 수 있지만, 해당 커맨드를 처리하는 방식은 소비자가 정의한다.
Passive-Aggressive Command 함정
- Event와 Command를 혼동하여 발생하는 대표적인 안티패턴이 Passive-Aggressive Command다.
- 이는 수신자가 특정 동작을 수행하기를 기대하면서도 Command 대신 Event 형태로 메시지를 발행하는 경우를 말한다.
- 예를 들어 "이메일을 보내라"는 명확한 Command를 사용해야 할 상황에서 "고객에게 새로운 견적에 대한 메시지가 필요합니다"와 같은 Event 형태로 메시지를 보내는 것이다.
- 이러한 패턴은 시스템의 의도를 불명확하게 만들고 유지보수를 어렵게 한다.
Event-Driven Architecture의 네 가지 패턴
마틴 파울러는 4가지 Pattern 을 통해 Event Driven Architecture 를 설명한다. 그 각각의 4가지 패턴을 통해서 EDA 가 구성되고, Event Driven System 은 이들 중 적어도 1가지는 갖춰야 한다고 말한다. 그렇다면 Event Driven System 을 구성하는 이 4가지 패턴에 대해서 하나씩 살펴보자.
Event Notification
Event Notification은 시스템이 관리하는 도메인의 변경 사항을 다른 시스템에 알리는 패턴이다. 핵심은 이벤트를 발행하는 Source System이 응답에 대해 크게 신경 쓰지 않는다는 점이다. Event Notification에서 이벤트는 최소한의 정보만 포함하는 경우가 많다. 보통 ID와 발행자에 대한 링크 정도만 담으며, 수신자는 추가 정보가 필요한 경우 발행자에게 다시 요청한다.
장점
- 이 패턴의 주요 장점은 낮은 결합도다.
- Source System과 Consumer System 간의 의존성이 최소화되므로 시스템 간 독립성이 높아진다. 설정도 비교적 간단하다.
단점
- 그러나 여러 이벤트 알림에 걸쳐 실행되는 논리적 흐름이 존재할 경우 문제가 발생할 수 있다.
- 각 이벤트 처리 로직은 명확하지만, 전체 비즈니스 흐름은 코드에 명시적으로 드러나지 않는다.
- 이러한 흐름은 실행 중인 시스템을 모니터링해야만 파악할 수 있으며, 이는 디버깅과 수정을 어렵게 만든다.
- Event Notification을 사용하면 잘 분리된 시스템을 쉽게 만들 수 있지만, 시간이 지날수록 전체적인 흐름을 놓치게 되어 나중에 문제가 발생할 수 있다.
적용 시나리오
- 느슨한 결합이 최우선일 때
- 이벤트에 대한 응답이 중요하지 않을 때
- 여러 Consumer가 동일한 이벤트를 각자 다른 방식으로 처리할 때
Event-Carried State Transfer
Event-Carried State Transfer는 Event Notification의 발전된 형태다. 핵심 차이는 이벤트에 포함되는 정보의 양이다.
Event Notification에서는 "Linda의 주소가 변경되었다"는 정보만 전달하지만, Event-Carried State Transfer에서는 "Linda의 주소가 A에서 B로 변경되었다"와 같이 상세한 상태 변경 정보를 포함한다. 더 나아가 Consumer System이 앞으로 필요할 수 있는 모든 고객 데이터의 복사본을 이벤트를 통해 전달할 수 있다.
장점
- Consumer System은 필요한 데이터를 로컬에 복제하여 보관하므로 Source System에 대한 의존성이 사라진다.
- 이는 여러 이점을 제공한다. 네트워크 지연이 제거되어 응답 속도가 빨라지고, Source System의 부하와 무관하게 동작할 수 있다.
- 또한 Source System이 장애 상황에서도 Consumer System은 복제된 데이터로 계속 작동할 수 있어 시스템 전체의 가용성이 향상된다.
단점
- 데이터 복제로 인한 저장 공간 증가와 데이터 정합성 관리의 복잡도가 증가한다. 결과적 일관성(Eventual Consistency)을 받아들여야 하며, Consumer System은 복제 데이터의 동기화와 유효성 관리에 대한 추가 로직이 필요하다.
- 마틴 파울러는 저장 공간 증가에 대해 현대의 스토리지 비용은 충분히 저렴하며, 모든 데이터를 복제하는 것이 아니라 필요한 데이터만 선택적으로 복제하므로 실제로는 큰 문제가 되지 않는다고 설명한다. 반면 데이터 정합성 문제는 신중하게 고려해야 할 요소다.
적용 시나리오
- Source System의 가용성이나 성능에 영향받지 않아야 할 때
- Consumer System이 Source System에 대한 네트워크 호출을 최소화해야 할 때
- 결과적 일관성을 허용할 수 있을 때
Event Sourcing
Event Sourcing은 시스템 상태 변경을 이벤트로 기록하고, 이벤트를 재처리하여 시스템 상태를 재구성할 수 있는 패턴이다. 이벤트 저장소가 주요 진실 공급원(Source of Truth)이 되며, 시스템 상태는 이벤트로부터 파생된다. 가장 좋은 예시는 버전 관리 시스템이다. Git의 커밋 로그가 이벤트 저장소이며, 작업 디렉토리가 시스템 상태에 해당한다.
Event Sourcing이 반드시 비동기적일 필요는 없다. 로컬 Git 저장소 업데이트는 완전히 동기적인 작업이다. 또한 모든 컴포넌트가 이벤트 로그를 직접 다룰 필요도 없다. 대부분의 처리는 작업 복사본을 기반으로 수행되며, 필요한 경우에만 이벤트 로그를 조회한다.
적용 시나리오
- 강력한 감사 기록이 필요할 때
- 과거 상태 재구성이 필요할 때
- 가상 시나리오 테스트가 필요할 때
CQRS
CQRS(Command Query Responsibility Segregation)는 읽기와 쓰기를 위한 데이터 구조를 분리하는 패턴이다. 엄밀히 말해 CQRS 자체는 이벤트와 직접적인 관련이 없지만, Event Sourcing과 함께 사용되는 경우가 많다.
복잡한 도메인에서 단일 모델로 읽기와 쓰기를 모두 처리하면 복잡도가 증가한다. CQRS는 이를 분리하여 각각을 최적화할 수 있게 한다. 특히 읽기가 많고 쓰기가 적은 패턴에서 유용하다. 그러나 CQRS 도입으로 인한 추가 복잡도를 고려해야 하며, 많은 경우 단일 모델로 충분하다.
적용 시나리오
- 읽기와 쓰기 패턴이 크게 다를 때
- 각각에 대한 최적화가 명확한 이점을 제공할 때
참고 출처
- https://martinfowler.com/articles/201701-event-driven.html
- https://martinfowler.com/eaaDev/EventNarrative.html
- https://medium.com/swlh/event-notification-vs-event-carried-state-transfer-2e4fdf8f6662
- https://notypie.dev/design-1-eda의-개념-1/
- https://viesure.io/events-vs-commands/developer/



