---
id: adr-0001-event-sourcing
title: "ADR-0001 이벤트 소싱 채택"
category: "Architecture_Decision"
status: "draft"
verification_status: "applied"
canonical_id: ""
aliases: ["ADR event sourcing", "왜 이벤트 소싱", "append-only 결정", "JSONL 결정"]
duplicate_of: ""
source_trust_level: "A"
confidence_score: 0.9
created_at: 2026-06-13
updated_at: 2026-06-13
review_reason: ""
merge_history: []
tags: ["adr", "event-sourcing", "decision", "persistence", "astraai"]
raw_sources: ["AstraAI/src/features/_shared/eventSourcedStore.ts", "AstraAI/src/memory/types.ts"]
applied_in: ["AstraAI"]
github_commit: ""
---

# [[ADR-0001 이벤트 소싱 채택]]

## 🎯 한 줄 통찰 (One-line insight)
도메인 데이터(고객·채용·런웨이·피드백)를 "상태 덮어쓰기" 대신 **append-only JSONL 이벤트**로 저장하기로 결정했다 — 이력 보존·내결함·중복 제거(제네릭 팩토리)를 한 번에 얻기 위해 [S1].

## 🧠 핵심 개념 (Core concepts)
- **결정:** `createEventStore<E>` 제네릭 팩토리로 모든 도메인 스토어를 append-only JSONL 위에 구현.
- **상태:** 현재 상태는 이벤트 재생(`computeStates`)으로 도출, 저장은 이벤트만.
- 자세한 구현은 [[이벤트 소싱 스토어 패턴]] 참조.

## 📖 세부 내용 (Details · ADR)
### Problem (문제)
customers/hire/runway/feedback 4개 도메인이 각자 `getXFilePath/readX/appendX/countX` 를 복붙해 ~240줄 byte-for-byte 중복. 동시에 "언제 무엇이 바뀌었나" 이력이 필요한 도메인인데 마지막 상태만 저장하면 그 정보를 잃는다 [S1].

### Context (맥락)
- 단일 사용자 로컬 VS Code 확장 — 무거운 DB 운영 비용을 감당할 이유가 적다.
- 데이터 규모가 작고(수백~수천 행), 사람이 직접 파일을 열어보는 투명성이 가치.
- 부분 손상(파일 1줄 깨짐)에도 나머지를 살려야 한다.

### Options Considered (고려한 대안)
1. **상태 JSON 1개 덮어쓰기** — 단순하지만 이력 손실·동시쓰기 시 전체 덮어쓰기 위험.
2. **SQLite** — 쿼리/인덱스 강력하지만 의존성·마이그레이션·운영 복잡도 추가.
3. **append-only JSONL 이벤트 + 제네릭 팩토리** — 이력 보존 + 한 줄 추가의 단순함 + 내결함.

### Chosen Solution (선택)
3번. `createEventStore<E>({ relPath, validate })` 가 read/append/count/getFilePath 를 제공하고, 도메인은 이벤트 타입 `E` 와 `computeStates` 만 정의 [S1].

### Why It Was Chosen (선택 이유)
- 이력이 그 자체로 가치 있는 도메인에 자연스럽다.
- 제네릭으로 4벌 중복을 1벌로 — BOM/인코딩 fix 도 한 곳에서 전파.
- 한 줄 손상이 전체를 무력화하지 않는 내결함(파싱 실패 줄 skip).
- 외부 의존 0 (Node fs 만) — 번들 가벼움 유지.

### Benefits (장점)
이력 감사·재현·디버깅 용이, 단순한 append I/O, 사람이 읽고 고칠 수 있는 투명성, 타입 안전한 재사용.

### Drawbacks (단점)
파일이 단조 증가(compaction 없음), 현재 상태를 매번 재생하는 비용, 멀티프로세스 동시 append 는 별도 잠금 필요.

### Future Risks (미래 위험)
- 이벤트 수가 수만을 넘으면 재생 비용/메모리 증가 → 스냅샷+증분 compaction 필요.
- 이벤트 스키마 진화 시 구버전 이벤트 호환(버전 필드/업캐스팅) 관리 부담.

### Alternative Approaches (대안 접근)
규모가 커지면 SQLite(+WAL)로 이전하거나, 이벤트는 유지하되 주기적 스냅샷을 도입. 읽기 빈도가 매우 높으면 메모리 캐시 + 파일 watch.

## ⚖️ 모순 및 업데이트 (Contradictions & updates)
"이력 보존" 이 목표가 아니라면(마지막 값만 중요) 이 결정은 과설계다. 그 경우 상태 1개 파일이 더 단순하다.

## 🛠️ 적용 사례 (Applied in summary)
`AstraAI/src/features/_shared/eventSourcedStore.ts` — 결정의 구현체 [S1].

## 🔗 지식 그래프 (Knowledge Graph)
- **상위/루트:** [[AstraAI 아키텍처 개요]]
- **관련 개념:** [[이벤트 소싱 스토어 패턴]], [[Event Bus Pattern]], [[ADR-0005 파일 기반 저장 채택]], [[엔지니어링 트레이드오프 분석]]
- **참조 맥락:** 로컬 LLM 이 "이력 저장 vs 상태 저장" 을 결정할 때 판단 근거로 참조.

## 📚 출처 (Sources)
- [S1] AstraAI/src/features/_shared/eventSourcedStore.ts — 제네릭 이벤트 스토어, 중복 통합 배경 주석
- [S2] AstraAI/src/memory/types.ts — 버전 필드 직렬화 스토어

## 📝 변경 이력 (Change history)
- 2026-06-13: AstraAI 코드 분석 기반 ADR 작성.
