일반적으로 database에는 우리의 비지니스 로직에 대한 정보를 담는다. 이는 곧 우리의 application에 대한 state(상태)를 나타낸다. 대부분의 경우 현재의 state를 나타내는 것이 훨씬 더 중요하기 때문에, 이전의 data에 대한 정보를 남겨두지 않는다. 즉, data가 수정되고, 삭제되고, 추가되었다는 이력보다도, 현재 어떤 값인가가 더 중요하다는 것이다.
가령, 삼겹살을 파는 이커머스 회사를 생각하보자. 현재의 삼겹살 가격이 DB에 저장되어 삼겹살에 대한 가격을 보여주는 것이 더 중요하지 이전에 얼마였는 가는 그렇게 중요하지 않다.
그러나, 은행의 경우는 좀 다르다. 은행의 경우 내가 잔금이 200만원이 있다면, 어떻게해서 200만원이 되었는 지에 대해서 확인을 해야할 때가 있다. 회사에 월급이 들어왔거나, 벌금을 물어 금액이 빠져나갔다던지 여러 가지 일들이 있었을 것이다. 여기에 대한 이력이 없고, 현재 state인 '잔금'만 있다면 사용자 경험에 있어서 굉장히 안좋은 경험을 전달해줄 수 있다.
이렇게 시각화, 검사, 정정을 위해서 application의 '현재의 상태'와 '모든 이전 상태'에 대해서 알아야하는 경우가 있을 것이다. 이때 사용하는 것이 바로 event sourcing pattern이다.
event sourcing pattern은 현재 상태를 저장하는 대신, 우리는 event들만 가지고 있는 것이다. 각 event들은 system 내 특정 entity에 대한 'change' 또는 'fact'를 설명하는 것이다.
가장 중요한 것은 event들은 immutable하다는 것이다. 즉 system에 event를 저장하면 이후에 event를 변경하지 않는다. 우리가 할 수 있는 일은 오직 event list 마지막에 새로운 event를 추가하는 것 뿐이다.
이렇게 entity
에 대한 event기록들을 남겨 놓으면, 현재 entity
에 대한 state는 여태까지의 event를 모두 적용하거나, 다시 재생하기만 하면 된다.
마치 git과 같이 동작한다고 생각해도 무방하다. 각 event들은 이전 state로부터의 차이 또는 변경 사항만을 나타내는 것이다.
가령, 은행 계좌의 경우, 현재 잔액을 유지 및 업데이트하는 대신 돈이 입금되거나 출금되는 거래만을 저장하는 것이다. 이 거래 내역을 통해 최신 잔금을 업데이트하는 것일 뿐이다. 이러한 거래 내역들을 통해서 감사와 잘못된 패턴, 사기 행위들을 찾아 낼 수 있는 것이다.
또한, 각 event들을 통해 추천시스템을 만들어 개인화 시스템을 구축할 수 있다.
첫번째는 event를 개별 record로 database에 저장하는 방법이다.
가령, 주문에 대한 기록을 database에 저장하는 것이다.
Order ID | State | Timestamp |
---|---|---|
10001 | PROCESSING | Jan 14 12:08 PM |
10001 | PAYMENT_COMPLETED | Jan 14 12:10 PM |
10001 | SHIPPED | Jan 15 8:03 AM |
이렇게 database에 event에 대한 기록을 record 형식으로 남겨놓는 것이다.
이렇게 database에 event를 저장하면 database의 도움을 받아, 전체 데이터 셋에 대한 query 및 분석을 쉽게 수행할 수 있다는 것이다.
문제는 microservice로 넘어가게 되면서 각 service마다 database가 따로 달려있게 되어서, 전체적인 event에 대한 query를 하고 합치기 쉽지 않다는 것이다.
이를 해결하기 위한 두 번째 방법이 바롷 Message Broker를 사용하는 것이다.
이 경우에는, 서비스에 속하는 개인 database에 event를 저장하는 대신에 event들을 소비하는 message broker가 그 기록들을 남겨놓는 것이다.
대부분의 database와 달리 message broker는 event를 처리하는 데 특히 최적화되어 있으며, 동일한 entity에 대한 서로 다른 event들을 중앙화되어 처리할 수 있다. 단, database보다 event query가 어렵고, 까다롭다는 문제가 있다.
오늘날의 event sourcing pattern에서 가장 많이 사용되는 것은 Message Broker이다. 즉, event를 관리하는 주체가 database가 아니라 message broker가 함으로 event sourcing을 담당한다는 것이다.
message broker로 event sourcing을 하는 가장 큰 이유는 write 연산에 대한 효율화이다.
다음의 '재고 관리 service'를 보도록 하자.
각 제품에 대한 재고 관련 event를 Inventory Database에서 처리한다고 하자. 이렇게 되는 경우 중앙화된 database에 동시에 너무 많은 write 경합이 발생하여 database 성능이 안좋아질 수 있다.
반면엔 message broker를 event sourcing으로 전환하면 event가 발생해도 database 잠금이 필요하지 않으며 message broker에 추가적으로 append만되므로 훨씬 더 효율적이다.
다음과 같이 각 service instance에서 event를 만들고 event source인 message broker에게 전달만 하면 된다.
그런데, event들은 각 entity에 대한 change, fact에 대한 이력이라고했다. 그렇다면 현재 상태를 얻어오기 위해서, 매번 event들을 replay하는 것은 효율적이지 않다. 어떻게하면 효율적으로 event들을 관리하고, event들을 통해서 현재 상태를 쉽게 제공할 수 있을까??
이전 시간에 배웠던 CQRS가 바로 이러한 문제를 효율적으로 해소해줄 수 있다.
event는 하나의 command이다. 따라서, command service의 database에 event들을 저장하고, event를 message broker에 보내도록 한다.
각 event들을 수신한 query service는 event를 받고, event에 해당하는 entity를 처리하여 database에 저장한다. 가령, 잔금이 1000원인데, '입금 1000원이라는 event'를 받으면, 잔금 entity에 대해서 2000원을 database에 저장하도록 하는 것이다.
사용자는 entity에 수정 명령을 command service로 보내고, 그 결과에 대한 값은 query service를 통해 읽기만 하면 되는 것이다. 이렇게하면 query service는 in-memory db나 NoSQL 처럼 읽기에 쉽고 효율적인 database를 사용하여 효율을 극대화할 수 있다.
정리해보면 command service의 database는 event만을 저장하여 event에 write 연산을 최적화할 수 있으며, 여기에 저장된 event들을 통해서 여러 비지니스 로직을 실현할 수 있다.
query service는 읽기 전용으로, 현재 비지니스 로직에 대한 상태를 가진 database를 가진다. 이를 통해서 client에게 evnet의 최종 상태인 현재 상태를 빠르게 보여줄 수 있다.
이러한 event sourcing pattern과 CQRS pattern의 결합은 실제로 이상 감지 시스템에서 event를 추적하여 잘못된 사용 기록을 확인해야하는 경우 많이 사용된다. 또한, 개인의 성향을 파악할 수 있도록 추천 시스템을 개발할 때 많이 사용된다.