현대의 분산 시스템과 마이크로서비스 아키텍처(MSA)는 데이터 관리의 복잡성을 크게 증가시키고 있습니다.
이러한 시스템에서는 다양한 서비스가 독립적으로 운영되며, 데이터 일관성과 성능 요구 사항을 동시에 충족해야 하는 도전 과제가 있습니다.
이를 해결하기 위해 이벤트 소싱(Event Sourcing)과 CQRS(Command Query Responsibility Segregation)와 같은 패턴이 널리 사용되고 있습니다.
이벤트 소싱(Event Sourcing)이란❓
이벤트 소싱은 시스템의 상태를 현재 상태로 저장하는 대신, 상태 변화(변경)를 나타내는 이벤트를 기록하는 패턴입니다. 이를 통해 시스템의 모든 상태 변경 이력을 저장하고, 이를 기반으로 현재 상태를 재구성할 수 있습니다.
이벤트 소싱의 주요 특징은 다음과 같습니다.
상태 변경을 이벤트로 기록
이벤트 소싱의 핵심 중 하나는 시스템의 데이터 변경 자체가 아닌, 상태 변경을 나타내는 이벤트를 기록하는 것입니다.
이벤트는 시스템에서 발생하는 모든 중요한 상태 변경을 캡처한 기록입니다. 예를 들어, 전자상거래 시스템에서는 주문 생성, 주문 상태 변경, 결제 처리 등의 모든 행동이 이벤트로 기록될 수 있습니다.
상태의 재구성
이벤트 소싱에서는 상태를 직접 저장하지 않고, 이벤트 로그를 통해 상태를 재구성합니다.
현재 상태는 과거에 발생한 모든 이벤트를 순차적으로 적용함으로써 재구성됩니다. 즉, 특정 시점의 상태를 알고 싶다면 해당 시점까지의 모든 이벤트를 재생하여 현재 상태를 계산합니다.
예시:
- 주문 생성
- 사용자가 주문을 생성하면 OrderCreated 이벤트가 발생하고 저장됩니다.
- 재고 예약
- 주문이 생성되면 InventoryReserved 이벤트가 발생합니다.
- 주문 발송
- 주문이 발송되면 OrderShipped 이벤트가 발생합니다.
이 모든 이벤트는 로그에 저장되고, 현재 주문 상태는 이러한 이벤트를 재생하여 결정됩니다.
이벤트의 불변성
이벤트 소싱에서 이벤트는 불변(immutable)하며, 한번 기록된 이벤트는 수정되거나 삭제되지 않습니다.
이 불변성은 데이터의 일관성과 무결성을 보장하는 데 중요한 역할을 합니다. 이벤트는 시간이 지나도 동일하게 유지되며, 이를 통해 데이터의 역사적 맥락을 보존할 수 있습니다.
이벤트 소싱(Event Sourcing)의 주요 개념
이벤트(Event)
이벤트는 시스템에서 발생한 상태 변경을 나타내는 불변의 기록입니다. 이벤트는 시스템의 상태가 변경된 것을 의미하며, 이 기록은 수정되거나 삭제되지 않습니다.
특징
- 불변성
- 이벤트는 한번 생성되면
변경되지 않습니다. 상태 변경의 원인을 그대로 유지하기 위해 불변성을 보장합니다.
- 이벤트는 한번 생성되면
- 기술적 세부사항 없음
- 이벤트는 상태 변경의 비즈니스 의미를 담고 있으며, 특정 기술적
세부사항을 포함하지 않습니다.
- 이벤트는 상태 변경의 비즈니스 의미를 담고 있으며, 특정 기술적
예시
- OrderCreated ➡️ 주문이 생성되었음을 나타내는 이벤트.
- InventoryReserved ➡️ 재고가 예약되었음을 나타내는 이벤트.
- PaymentProcessed ➡️ 결제가 성공적으로 처리되었음을 나타내는 이벤트.
이벤트 스토어(Event Store)
이벤트 스토어는 이벤트를 저장하는 데이터 저장소입니다. 이는 이벤트가 순서대로 저장되며, 언제든지 해당 이벤트를 조회하거나 재생할 수 있도록 합니다.
특징
- 순서 보장
- 이벤트는 발생 순서대로 저장되며, 이를 통해 시스템의 상태를 정확히 재구성할 수 있습니다.
- 내구성
- 이벤트는 영구적으로 저장되며, 시스템 장애나 재시작 시에도 데이터를 보존합니다.
예시
- 데이터베이스: 이벤트를 저장하는 전용 데이터베이스. (ex: Apache Kafka, EventStoreDB)
- 메시지 큐: 이벤트를 전송하고 저장하는 메시지 큐 시스템. (ex: RabbitMQ, Amazon SQS)
커맨드 (Command)
커맨드는 시스템의 상태를 변경하기 위해 발행되는 요청입니다. 커맨드는 상태 변경을 지시하는 명령으로, 비즈니스 로직을 실행하여 이벤트를 생성합니다.
특징
- 상태 변경 요청
- 커맨드는 시스템의 상태를 변경하기 위해 사용됩니다.
- 무결성
- 커맨드는 비즈니스 로직에 의해 검증되어야 하며,
유효하지 않으면상태 변경이 발생하지 않습니다.
- 커맨드는 비즈니스 로직에 의해 검증되어야 하며,
예시
- CreateOrderCommand ➡️새로운 주문을 생성하기 위한 커맨드.
- ReserveInventoryCommand ➡️ 주문의 재고를 예약하기 위한 커맨드.
- ProcessPaymentCommand ➡️ 결제를 처리하기 위한 커맨드.
이벤트 핸들러(Event Handler)
이벤트 핸들러는 이벤트가 발생했을 때 이를 처리하는 컴포넌트입니다. 이벤트 핸들러는 이벤트를 수신하고, 필요한 작업을 수행하거나 시스템의 상태를 업데이트합니다.
특징
- 비즈니스 로직 실행
- 이벤트 핸들러는 이벤트에 따라 비즈니스 로직을 실행합니다.
- 비동기 처리
- 이벤트 핸들러는 비동기적으로 동작할 수 있으며, 이벤트 발생 후
즉시 처리되지 않을 수 있습니다.
- 이벤트 핸들러는 비동기적으로 동작할 수 있으며, 이벤트 발생 후
예시
- OrderCreatedHandler ➡️ OrderCreated 이벤트가 발생했을 때 주문 생성 로직을 실행합니다.
- InventoryReservedHandler ➡️ InventoryReserved 이벤트를 처리하여 재고 상태를 업데이트합니다.
프로젝션 (Projection)
프로젝션은 이벤트를 기반으로 특정 뷰나 상태를 생성하는 과정을 말합니다. 이는 이벤트의 집합을 기반으로 읽기 전용 데이터 모델을 만들어 효율적인 조회를 가능하게 합니다.
특징
- 읽기 최적화
- 프로젝션은 특정 쿼리나 읽기 작업을 최적화된 방식으로 처리합니다.
- 상태의 재구성
- 이벤트를 통해 특정 시점의 상태를 재구성하여 조회할 수 있습니다.
예시
- OrderSummaryProjection: 모든 주문의 요약 정보를 생성하여 조회할 수 있는 데이터 모델입니다.
- CustomerOrderHistoryProjection: 특정 고객의 주문 이력을 조회할 수 있는 데이터 모델입니다.
이벤트 소싱(Event Sourcing)의 장단점
장점
- 완전한 변경 이력
- 모든 상태 변경이 기록되므로, 데이터의 역사적 상태를 완벽하게 추적할 수 있습니다.
- 디버깅과 감사
- 문제 발생 시 상태 변경 이력을 기반으로 원인을 추적하고 디버깅할 수 있습니다.
- 재구성
- 데이터 모델이 변경되더라도 이벤트를 재생하여 새로운 모델로 상태를 재구성할 수 있습니다.
- CQRS와의 자연스러운 통합
- 이벤트 소싱은 CQRS(Command Query Responsibility Segregation)와 잘 어울립니다.
- 명령과 조회를 분리하여 성능과 확장성을 최적화할 수 있습니다.
단점
- 복잡성 증가
- 이벤트의 관리와 저장이 추가되면서 시스템의 복잡성이 증가합니다.
- 성능 고려
- 이벤트를 재생하여 현재 상태를 생성하는 데 시간이 소요될 수 있습니다.
CQRS(Command Query Responsibility Segregation)이란❓
CQRS는 쓰기 ➡️ 명령(Command)과 읽기 ➡️ 쿼리(Query) 작업을 분리하는 패턴입니다.
명령(Command)
- 데이터를 변경하는 작업입니다.
- 예를 들어, 사용자가 주문을 생성하거나 결제 정보를 업데이트하는 작업이 이에 해당합니다.
- 명령은 비즈니스 로직을 수행하고, 데이터베이스를 업데이트하며, 상태 변경을 반영합니다.
쿼리 (Query)
- 데이터를 조회하는 작업입니다.
- 예를 들어, 사용자의 주문 내역을 조회하거나 상품 목록을 검색하는 작업이 이에 해당합니다.
- 쿼리는 읽기 전용 작업으로, 데이터베이스에서 필요한 정보를 조회하고 반환합니다.
독립적인 모델
CQRS는 명령과 쿼리 각각에 대해 독립적인 데이터 모델을 사용합니다. 이러한 모델 분리는 각 모델을 최적화하여 성능을 극대화하고, 데이터의 일관성을 유지하는 데 도움을 줍니다.
- 커맨드 모델
- 쓰기 작업에 최적화된 데이터 모델입니다.
- 데이터의 무결성과 비즈니스 규칙을 유지하는 데 중점을 둡니다.
- 쿼리 모델
- 읽기 작업에 최적화된 데이터 모델입니다.
- 빠른 조회를 가능하게 하고, 복잡한 쿼리를 효율적으로 처리합니다.
CQRS의 장단점
장점
- 성능 최적화
- 읽기와 쓰기 작업을 독립적으로 최적화할 수 있어, 성능을 극대화할 수 있습니다.
- 확장성
- 읽기와 쓰기 작업을 별도로 확장할 수 있어, 트래픽 증가에 효과적으로 대응할 수 있습니다.
- 복잡성 관리
- 비즈니스 로직과 데이터 조회를 분리하여 복잡성을 줄일 수 있습니다.
단점
- 일관성 문제
- 읽기와 쓰기 모델이 분리되어 있어 데이터 일관성을 유지하는 데 추가적인 노력이 필요합니다.
- 복잡한 설계
- 모델 분리로 인해 설계와 구현이 복잡해질 수 있습니다.
이벤트 소싱과 CQRS의 결합
명령과 쿼리 모델이 분리되어 있기 때문에, 이들 간의 데이터 동기화가 필요합니다.
데이터 동기화는 두 가지 모델이 일관된 상태를 유지하도록 보장하는 과정입니다. 이는 일반적으로 이벤트 소싱과 결합하여 처리되며, 이벤트를 통해 상태 변경을 쿼리 모델에 반영합니다.
예시
- 명령 처리(Write)
- 사용자가 새로운 주문을 생성하면, 커맨드 모델이 상태를 업데이트하고 OrderCreated 이벤트를 발생시킵니다.
- 쿼리 처리(Read)
- 사용자가 자신의 주문 내역을 조회하면, 쿼리 모델이 최신 상태를 반영하여 결과를 반환합니다.
이벤트 소싱과 CQRS는 서로 보완적인 패턴으로, 함께 사용하면 강력한 데이터 관리 설루션을 제공합니다. 이벤트 소싱을 통해 모든 상태 변경 이력을 기록하고, CQRS를 통해 읽기와 쓰기 작업을 최적화하여 성능을 개선할 수 있습니다.
아키텍처로 보는 이벤트 소싱과 CQRS의 결합
- Client
- 클라이언트는 사용자 인터페이스 또는 다른 시스템에서 오는 요청을 처리합니다.
- 이 요청은 시스템에 대한 명령(Command) 또는 쿼리(Query)를 포함할 수 있습니다.
- Command
- 명령은 시스템의 상태를 변경하는 작업을 요청합니다. ➡️ 쓰기(Write)
- 예를 들어, '주문 생성', '계좌 이체' 등이 명령에 해당합니다.
- Command Handler
- 명령 핸들러는 명령을 받아서 이를 처리하는 책임을 지고 있습니다.
- Command Handler는 비즈니스 로직을 실행하고 상태 변경을 수행합니다. ➡️ 쓰기(Write)
- Domain Model
- 도메인 모델은 비즈니스 로직과 규칙을 정의합니다.
- Command Handler는 도메인 모델을 통해 상태를 변경합니다.
- Event
- 도메인 모델에서 상태가 변경되면, 이 변경을 나타내는 이벤트가 생성됩니다.
- 이벤트는 상태 변경의 기록을 담고 있으며, 시스템의 상태를 추적하는 데 사용됩니다.
- Event Store
- 이벤트 스토어는 발생한 이벤트를 저장하는 저장소입니다.
- 이벤트는 상태 변경을 나타내는 불변의 기록으로 저장됩니다.
- Event Handlers
- 이벤트 핸들러는 이벤트를 처리하고, 이벤트에 따라 다른 작업을 수행합니다.
- 예를 들어, 이벤트가 발생하면 다른 시스템에 알리거나 추가적인 처리를 수행할 수 있습니다.
- Query
- 쿼리는 시스템의 상태를 조회하는 요청입니다. ➡️ 읽기(Read)
- 예를 들어, '사용자의 주문 내역 조회', '계좌 잔액 조회' 등이 쿼리에 해당합니다.
- Query Handlers
- 쿼리 핸들러는 쿼리를 받아서 데이터를 조회하고 응답을 생성합니다.
- 쿼리 핸들러는 읽기 전용 작업을 수행합니다.
- Query Database
- 쿼리 DB는 쿼리 핸들러가 데이터를 조회할 때 사용하는 데이터베이스입니다. 즉, 조회 쿼리를 날릴 때 DB
- 이 데이터베이스는 이벤트가 발생한 후에 업데이트됩니다.
'Architecture > MSA' 카테고리의 다른 글
[MSA] RabbitMQ와 Saga 패턴을 활용한 MSA의 트랜잭션 시뮬레이션 관리 및 에러 처리 (0) | 2024.08.17 |
---|---|
[MSA] 분산 시스템에서 2단계 커밋(2PC)과 SAGA 패턴을 통한 데이터 일관성 유지 (0) | 2024.08.14 |
[MSA] MSA란 무엇일까❓ (1) | 2024.07.31 |