반응형
Event Sourcing 은 데이터 상태를 단순히 현재 상태로 저장하는 대신, 상태 변화를 일으킨 모든 이벤트를 순차적으로 저장하는 아키텍처 패턴입니다.
이 방식은 기존의 CRUD 기반 시스템과 다르게 데이터 변경의 이력(history) 을 완전하게 기록하며, 이를 기반으로 현재 상태를 복원합니다.
-
이벤트(event) 중심의 데이터 저장
- 시스템에서 발생하는 중요한 모든 변경 사항을 이벤트로 정의합니다.
- 예: 사용자가 계좌에 돈을 입금 →
DepositMade
이벤트로 기록.
-
현재 상태를 재구성
- 데이터를 읽을 때는 저장된 이벤트를 시간 순서대로 재생(replay)하여 현재 상태를 복원합니다.
-
이벤트 로그(Event Log)
- 이벤트는 불변(immutable)으로 저장됩니다.
- 각 이벤트는 시스템의 상태 변화만 기술하며, 변경 이전의 상태는 덮어쓰지 않습니다.
계좌 ID: 12345
잔고: 500원
- 마지막 상태만 저장.
[이벤트 로그]
1. AccountCreated(accountId=12345)
2. DepositMade(accountId=12345, amount=300)
3. DepositMade(accountId=12345, amount=700)
4. WithdrawalMade(accountId=12345, amount=500)
- 모든 상태 변화가 이벤트로 기록됩니다.
- 현재 잔고는 이벤트를 "재생(Replay)"하여 계산:
300 + 700 - 500 = 500
-
완전한 감사(audit) 및 추적 가능성
- 모든 변경 사항을 이벤트로 기록하므로, 데이터 변경의 원인을 쉽게 추적할 수 있습니다.
-
재생 가능
- 이벤트 로그를 다시 재생하여 과거의 특정 상태를 복원할 수 있습니다.
- 시스템 장애 발생 시 현재 상태를 빠르게 복구 가능.
-
분산 시스템에 적합
- 이벤트는 독립적이고 불변이므로, 메시징 시스템을 통해 다른 서비스와 쉽게 공유할 수 있습니다.
-
CQRS(Command Query Responsibility Segregation)와의 궁합
- Event Sourcing은 CQRS 패턴 과 함께 사용하면, 읽기/쓰기 모델을 최적화 하고 고성능 시스템을 설계할 수 있습니다.
-
복잡성 증가
- 이벤트를 관리하는 추가적인 작업이 필요합니다.
- 이벤트 스키마 변경 시, 기존 로그와의 호환성 유지가 어려울 수 있습니다.
-
읽기 성능 저하
- 현재 상태를 빠르게 복원하려면 많은 이벤트를 재생해야 하므로 읽기 성능이 저하될 수 있습니다.
- 이를 해결하기 위해 스냅샷(snapshot) 기법을 사용합니다.
-
디버깅 난이도
- 버그가 발생할 경우, 이벤트 로그를 일일이 분석해야 합니다.
kotlin
// 이벤트 정의
sealed class AccountEvent
data class AccountCreated(val accountId: String) : AccountEvent()
data class DepositMade(val accountId: String, val amount: Int) : AccountEvent()
data class WithdrawalMade(val accountId: String, val amount: Int) : AccountEvent()
// 현재 상태 복원
data class Account(val accountId: String, var balance: Int = 0) {
fun apply(event: AccountEvent) {
when (event) {
is AccountCreated -> println("Account created: ${event.accountId}")
is DepositMade -> balance += event.amount
is WithdrawalMade -> balance -= event.amount
}
}
}
// 이벤트 재생
fun replayEvents(accountId: String, events: List<AccountEvent>): Account {
val account = Account(accountId)
events.forEach { event -> account.apply(event) }
return account
}
// 예제 실행
val events = listOf(
AccountCreated("12345"),
DepositMade("12345", 500),
DepositMade("12345", 300),
WithdrawalMade("12345", 200)
)
val account = replayEvents("12345", events)
println("최종 잔고: ${account.balance}") // 출력: 최종 잔고: 600
- 금융 시스템: 거래 내역 및 잔고 관리.
- 전자상거래: 주문 상태 추적.
- IoT: 센서 데이터 이력 관리.
- 분산 시스템: 마이크로서비스 간 이벤트 기반 통신.
Event Sourcing은 시스템의 투명성을 높이고 복잡한 비즈니스 로직을 효율적으로 관리할 수 있는 강력한 패턴이지만, 적용 전 충분한 고려와 설계가 필요합니다.
728x90
반응형
'Designing Software' 카테고리의 다른 글
멀티테넌트(Multi-Tenant) 아키텍처 (0) | 2025.02.21 |
---|---|
BFF(Backend for Frontend) 패턴 (0) | 2025.02.21 |
이벤트 소싱(Event Sourcing) 패턴 (0) | 2024.11.17 |
마이크로서비스 패턴 (Microservices Pattern) (0) | 2024.11.17 |
레이어드 패턴 (Layered Pattern) (0) | 2024.11.17 |