무엇인가
발명이라기보다 재발견이다. 복식부기 원장은 수백 년 전부터 잔액을 고치지 않고 거래를 추가해 왔고, 은행 계좌의 잔액은 저장된 값이 아니라 거래 내역의 합이다. 데이터베이스 내부도 언제나 그렇게 동작했다 — 쓰기 로그(WAL)가 진실이고 테이블은 그 캐시다. 이 오래된 규율을 애플리케이션 설계 패턴으로 정리한 것이 Martin Fowler 의 Event Sourcing 항목(2000년대 중반)이고, Greg Young 이 CQRS 와 한 쌍으로 묶어 보급했으며, Martin Kleppmann 이 2014년 강연 "Turning the Database Inside-Out"에서 아키텍처 전체로 뒤집어 놓았다 — DB 가 속에 감춰 온 로그+파생 뷰 구조를 밖으로 꺼내, 로그를 시스템의 정본으로 삼자는 제안이다.
| 용어 | 뜻 | 비고 |
|---|---|---|
| 이벤트(Event) | 일어난 일의 불변 기록 — 과거형으로 서술 | 한 번 쓰면 고치지 않는다 |
| 로그(Log) | 이벤트의 append-only 열 — 시스템의 정본 | 삭제·수정 없음, 추가만 |
| 프로젝션(Projection) | 로그에서 계산한 읽기 모델(뷰) | 언제든 버리고 재계산 가능 |
| 스냅샷(Snapshot) | 어느 시점까지의 재생 결과를 저장해 둔 것 | 순수 최적화 — 정본이 아니다 |
| 재생(Replay) | 스냅샷 + 이후 이벤트를 순서대로 적용해 상태 복원 | 같은 입력이면 같은 결과(결정성) |
| 보정 이벤트 | 과거를 고치는 대신 정정 사실을 새로 추가 | 회계의 정정 분개와 동형 |
대가로 얻는 것이 셋이다. 완전한 감사 이력(무엇이 언제 왜 바뀌었는지 전부 남음), 시간여행 질의(임의 시점의 상태를 as-of 재생으로 복원), 새 해석의 소급 적용(새 프로젝션을 정의하면 과거 전체가 즉시 그 관점으로 다시 읽힘). 이 전통을 데이터베이스 제품으로 밀고 간 것이 Datomic(Rich Hickey, 2012 — 사실(datom)을 축적만 하고 트랜잭션 시간을 축으로 as-of 질의)과 XTDB(구 Crux — 유효 시간과 기록 시간 두 축을 모두 갖는 bitemporal)다. 요컨대 사실+시간의 데이터베이스 — "무엇이 있는가"가 아니라 "무엇이 언제 참이 되었는가"를 저장한다.
우리 모델과의 접점
개념 모델의 원칙 7 — 저장보다 파생 — 은 이 계보의 직계다. 시제(원칙 2)·롤업·순자산 곡선·미배정 자금을 전부 저장하지 않고 계산하며, 데이터 모델의 곡선 공식 "스냅샷(시작점) + 반복 Flow(계단) + 일회 Flow(점프)"는 문자 그대로 스냅샷+재생이다.
| 이벤트 소싱 | 우리 모델 | 비고 |
|---|---|---|
| append-only 사실 | Flow · (v2) Transaction | 돈의 이동이 우리 모델에서 가장 순수한 '이벤트' |
| 프로젝션 | 렌즈(필터+투영) · 시제 · 롤업 | 렌즈는 문이지 창고가 아니다(원칙 1) |
| 스냅샷 + 재생 | NetWorthSnapshot + Flow 누적 = 곡선 | 단, 스냅샷의 지위가 다르다 — 아래 어긋남 ② |
| 델타 적용 | 시나리오 겹: 확정 세계 − 가림 + 델타 | 렌더는 계산, 승격은 채택 델타만 합류 |
| 보정 이벤트 | 없음 — 사건은 제자리 수정 | 어긋남 ① |
| as-of · bitemporal | 없음 — 사건의 시간 한 축뿐 | 생각할 거리 2 |
겹(시나리오)의 합성이 특히 이 계보의 표준 패턴이다 — 확정 세계라는 베이스 위에 시나리오의 델타 묶음을 얹어 렌더하고, 결정(③)의 순간 채택 시나리오의 델타만 확정 세계로 승격하며, 기각 시나리오는 discarded 아래 보존된다. 저장은 델타, 화면은 재생, 이력은 불변 — 이벤트 소싱 시스템의 브랜치 병합과 같은 뼈대다.
대응만큼 어긋남이 유익하다:
- ① '사건'은 동음이의어다. 이벤트 소싱의 event 는 불변의 변경 기록이지만, 우리의 사건(Event)은 수정 가능한 도메인 객체 — 언제든 고쳐 쓰는 인생의 한 문장이다. 우리가 이 전통에서 물려받은 것은 '사건'이라는 낱말이 아니라 파생의 규율(정본은 하나, 해석은 계산) 쪽이다. 낱말이 같다고 구조를 같다고 착각하면 안 된다.
- ② 스냅샷의 지위가 반대다. 이벤트 소싱의 스냅샷은 언제든 버리고 재계산할 수 있는 캐시지만, 우리의 NetWorthSnapshot 은 사용자가 실측해 입력한 원천 데이터이자 곡선의 시작점이다. 삶의 로그는 완전할 수 없으므로(모든 거래를 적지 않으므로) 스냅샷을 정본으로 삼는 실용적 타협 — 로그 순수주의를 버린 자리가 우리 모델이 '기록 도구'라는 정체성을 보여 준다.
Transaction(Flow 의 실측화)은 이 계보의 약속을 우리 버전으로 실현한다 — 추정 반복 Flow 는 실측으로 대체되고, 배정은 지출 시 자동 차감되는 실측 잔액으로 격상되는데, 프로젝션(순자산 곡선)은 한 줄도 바뀌지 않은 채 같은 계산이 그대로 정확해진다. 뷰가 정본이 아니라 파생물이기에 가능한, 저장보다 파생(원칙 7)의 배당금이다.생각할 거리
- 문장의 편집 이력을 로그로 남길 것인가. 지금 사건 수정은 제자리 덮어쓰기라 "이 문장을 언제 어떻게 고쳐 왔는가"가 사라진다. 회고 도구에서 서술의 변천 자체가 자료일 수 있지만(서사 정체성의 핵심 주장), 편집 이력은 새 개념이므로 원칙 10의 일곱 질문을 통과해야 한다 — 백업(zip)의 세대 보관으로 값싸게 대신할 수 있는지부터 물을 것. Git 브랜칭 모델과 같은 선상의 질문.
- 일어난 때와 적은 때 — 둘째 시간축. 회고 기록은 필연적으로 유효 시간(사건의 때)과 기록 시간(적은 때)이 수십 년 벌어진다 — XTDB 가 bitemporal 을 두는 바로 그 이유다. 원칙 2는 사건의 시간 한 축만 저장하는데, 기록 시점이 남으면 "쉰에 돌아본 스물"과 "서른에 돌아본 스물"을 구분할 수 있다(회고 절정 분석의 전제이기도 하다). 성찰(Reflection)의 작성 시각이 이 둘째 축의 씨앗인가.
- 스냅샷과 로그가 어긋나면 누가 이기나. 이벤트 소싱이라면 로그가 정본이지만 우리는 스냅샷이 정본이다(어긋남 ②). 새 실측 스냅샷이 직전 스냅샷+Flow 누적의 예측과 다를 때 — 그 차액을 조용히 삼킬지, "적지 않은 이동이 있었다"는 보정 Flow 로 드러낼지는 v2 Transaction 정산 설계의 첫 질문이다. 스톡·플로우의 회계 항등식 문제와 같은 자리.
- 겹 합성은 결정적인가. 재생의 힘은 같은 입력이면 같은 결과라는 결정성에서 나온다. 시나리오 겹의 합성(확정 세계 − 가림 + 델타, 참조는 수정판 우선)이 겹 중첩·순서에 대해 결정적으로 정의돼 있는지 — 특히 여러 시나리오를 나란히 견주는 검토(②) 화면에서 — 를 CQRS / 읽기 모델의 프로젝션 규율로 점검할 것.
더 찾아보기
- Martin Fowler, Event Sourcing — 패턴의 표준 정리. 짝 글 검색:
Fowler "Focusing on Events". - Martin Kleppmann — 검색:
Kleppmann "Turning the Database Inside-Out"(2014 강연). 같은 사상의 책이 Designing Data-Intensive Applications(O'Reilly, 2017), 특히 파생 데이터를 다루는 마지막 장. - 검색:
Greg Young CQRS event sourcing— 패턴을 보급한 실무 문헌·강연들.Pat Helland "Immutability Changes Everything"— 불변성 옹호의 짧고 강한 논문. - datomic.com · xtdb.com — 사실+시간 데이터베이스의 두 실물.
- 같은 계보의 이웃: CQRS / 읽기 모델(쓰기와 읽기의 분리 — 렌즈의 기술판), Git 브랜칭 모델(불변 이력 위의 갈림길 — 시나리오의 사촌), 플레인텍스트 회계(append-only 원장의 개인 재무판), Local-first software(Kleppmann 의 다른 얼굴 — 기록의 소유).
내 생각
이 메모는 지도 페이지의 같은 항목과 공유되며, 이 기기(브라우저)에 저장됩니다.