이벤트 소싱은 아직 낮선 데이터 저장 기법이지만 클라우드에서 구동되는 반응형 시스템에 적합하기 때문에 주목받고 있습니다. 하지만 최종 상태만을 저장하는 기존 방식에 익숙한 프로그래머에게 이벤트 소싱을 익히고 활용을 극대화하는 것은 결코 쉽지 않습니다. 이 글은 전통적 데이터 저장 방식과 비교해 이벤트 소싱의 핵심적인 특징을 소개합니다.

 

전통적 방식과 이벤트 소싱(Event Sourcing) 비교

관계형 데이터베이스를 사용하던 NoSQL 데이터베이스를 사용하던 시스템의 주요 데이터를 유지하기 위해 최종 상태를 저장하는 방식은 오랜 기간 주류로 자리잡아 왔습니다. 이 경우 일반적으로 시스템 변경 기록은 완벽한 일관성을 보장하지 않고 경우에 따라 일정 시간 유지된 후 영구삭제되기도 합니다.

예를 들어 관계형 데이터베이스에 저장되는 주문 집합체(aggregate)를 떠올려봅니다. 주문 집합체는 주문 엔터티와 주문항목 등의 하위 엔터티를 포함합니다. 각 엔터티는 외래 키(foreign key)로 연결됩니다. 주문항목 추가 명령이 주문 집합체에 전달되면 도메인 모델은 주문 집합체가 저장된 데이터베이스에 주문항목 엔티티를 추가하고, 주문항목 수량을 변경하는 명령이 전달되면 데이터베이스에서 기존 주문항목 엔터티의 상태를 갱신합니다. 비슷하게 주문항목 삭제 명령에 대해선 데이터베이스에서 기준 주문항목 엔터티를 삭제합니다. 즉, 데이터베이스에는 항상 주문 집합체의 최종 상태가 저장됩니다.

Orders Table

| Id   |
| ---- |
| 7dd8 |

OrderItems Table

| OrderId | ItemId | Quantity |
| ------- | ------ | -------- |
| 7dd8    | 9a37   | 5        |
| 7dd8    | a974   | 10       |

반면 이벤트 소싱은 도메인에서 발생하는 이벤트의 순차적 기록을 일급 데이터로 다루며 이런 이벤트를 ‘도메인 이벤트’라 부릅니다. 이벤트 소싱에서 도메인 모델이나 엔터티의 상태는 초기 시점부터 현재까지 발생한 모든 도메인 이벤트의 결과물입니다. 도메인 이벤트는 집합체에서 발생해 이벤트 저장소에 기록됩니다. 그리고 집합체가 복원될 때 재생되어 집합체의 현재 상태를 묘사합니다. 도메인 이벤트는 이미 과거에 일어난 사건이기 때문에 수정되거나 삭제되지 않습니다.

이벤트 소싱에서 주문 집합체는 앞서의 사례와는 다른 방식으로 관리됩니다. 주문 집합체를 위해 실행되는 데이터베이스 연산은 삽입(insert) 뿐입니다. 주문항목의 수량이 변경된 것도, 주문항목이 삭제된 것도 모두 새로운 도메인 이벤트이기 때문에 갱신(update)이나 삭제(delete) 연산은 수행되지 않습니다. 도메인 모델 데이터베이스에는 주문 집합체의 상태가 아니라 그동안 발생한 도메인 이벤트 기록이 저장됩니다.

OrderEvents Table

| OrderId | Version | EventType                | EventData                  |
| ------- | ------- | ------------------------ | -------------------------- |
| 7dd8    | 1       | OrderPlaced              | OrderId: 7dd8              |
| 7dd8    | 2       | OrderItemAdded           | ItemId: 9a37, Quantity: 3  |
| 7dd8    | 3       | OrderItemAdded           | ItemId: c52a, Quantity: 1  |
| 7dd8    | 4       | OrderItemDeleted         | ItemId: c52a               |
| 7dd8    | 5       | OrderItemAdded           | ItemId: a974, Quantity: 10 |
| 7dd8    | 6       | OrderItemQuantityChanged | ItemId: 9a37, Quantity: 5  |

이벤트 소싱의 장점과 단점

이벤트 소싱은 다음과 같은 장점을 가집니다.

  • 개체-관계형 임피던스 불일치가 존재하지 않습니다. 이벤트 소싱에서 집합체의 개체 모델은 영속장치에 저장되기 위해 관계형 모델로 매핑되지 않습니다.
  • 신뢰할 수 있는 시스템 기록을 확보할 수 있습니다. 도메인 모델의 모든 변경 사항은 도메인 이벤트로 기록됩니다. 시스템 오류가 발생하면 프로그래머는 오류를 수정하기 위해 도메인 이벤트를 통해 오류가 발생한 시점의 도메인 모델을 복원할 수 있습니다.
  • 메시지 중심(message-driven) 아키텍처에 적합합니다. 도메인 이벤트는 일급 데이터로 저장되기 때문에 이를 통해 최소 일 회 배달(at-least-once delivery)을 구현하기 용이합니다. 도메인 모델에서 발생한 이벤트는 모니터될 수 있으며 반드시 발행됨을 보장할 수 있습니다.
  • 저장소 규모를 확장하기 쉽습니다. 도메인 이벤트 데이터베이스를 분할하기 위해 필요한 유일한 분할 키(partition key)는 집합체 식별자입니다.
  • 도메인 이벤트는 수정되거나 삭제되지 않으며 오직 추가만 되기 때문에 일반적으로 기존 데이터에 접근하기 위한 경쟁이 발생하지 않습니다.

반면 단점 또한 존재합니다.

  • 조회 작업에 적합하지 않습니다. 이벤트 저장소는 비즈니스의 다양한 데이터 조회 요구를 수용하기 어렵습니다. 이런 문제를 해결하기 위해 이벤트 소싱은 항상 CQRS(Command and Query Responsibility Segregation)와 함께 사용됩니다.
  • 아직 많이 낯섭니다. 많은 프로그래머들은 상태를 성공적으로 변경한 후 이에 대한 적절한 이벤트를 발생시키는 절차에 익숙합니다. 하지만 이벤트 소싱에선 상태 변경은 성공적인 이벤트 발생의 결과입니다.
  • 오랜 기간 성숙되어온 기존 방식에 비해 상대적으로 도구가 부족합니다.