1. 전통적인 CRUD 아키텍처의 한계
1-1. 명령과 조회가 뒤엉킨 서비스 구조
전통적인 애플리케이션은 대부분 하나의 도메인 모델로 명령(Command)과 조회(Query)을 동시에 처리한다. 예를 들어 OrderService는 주문 생성과 동시에 주문 내역 조회도 담당하며, 이는 단일 모델로 모든 역할을 떠안는 구조다. 개발 초기에는 간단해 보일 수 있으나, 시간이 지나면 기능이 복잡해지고 데이터 처리 요구가 다양해지면서 코드의 응집도가 낮아지고 유지보수가 어려워진다.
게다가 명령과 조회는 설계 목표 자체가 다르다. 명령은 데이터 정합성과 비즈니스 규칙을 중심으로 설계되어야 하고, 조회는 성능과 응답 속도에 초점을 맞춰야 한다. 두 책임을 한 클래스가 동시에 지는 것은 단일 책임 원칙(SRP)을 위배하는 결과로 이어진다.
따라서 이러한 문제의 근본 원인은 명령과 조회를 구분하지 않는 구조에 있다. CQRS(Command Query Responsibility Segregation)는 이 문제를 해결하기 위한 첫 번째 설계적 대안이다.
1-2. 쓰기 모델에 최적화된 도메인 객체
전통적인 방식에서는 엔티티나 애그리거트가 저장되는 데이터베이스 구조에 의존적으로 구성된다. 이 때문에 쓰기 모델은 종종 DB 테이블의 구조를 그대로 반영하며, 객체 지향적 설계보다는 정규화된 데이터 중심 모델에 가까워진다. 이 구조에서는 상태 변경 로직이 도메인 규칙과 섞이기 쉽고, 복잡한 상태 전이는 서비스 계층에서 구현되기 시작한다. 이로 인해 도메인 모델이 점점 얇아지고, 비즈니스 로직은 산발적으로 흩어진다. 게다가 이러한 엔티티 기반의 쓰기 모델은 조회 용도로 활용하기에 불편한 구조를 가지고 있으며, 결국 View에 필요한 데이터를 맞추기 위해 복잡한 조인이나 계산 로직이 서비스 계층에 추가된다.
1-3. 조회 성능 병목과 모델 왜곡
단일 도메인 모델로 조회까지 처리하는 구조는 대부분 성능의 병목으로 이어진다. 예를 들어 상품 상세 페이지를 출력하는 데 수십 개의 테이블 조인이 필요하거나, 조건에 따라 데이터를 가공해 전달하는 과정을 매번 DB 레벨에서 처리해야 한다면, 시스템은 점점 느려질 수밖에 없다.
이를 해결하기 위해 DTO(Data Transfer Object)를 사용하거나, 쿼리 전용 SQL을 작성하지만 이는 도메인과 분리된 구조가 되고, 테스트와 유지보수가 어려워진다. 결국 개발자들은 조회 모델을 따로 만들어야겠다는 필요성을 느끼게 된다. 이러한 자연스러운 흐름이 바로 CQRS의 기초가 되는 발상이다. 즉, 쓰기와 읽기를 아예 다른 모델로 분리하고 각자의 책임에 집중하게 하자는 것이다.
1-4. 이벤트가 사라지는 구조
전통적인 CRUD 시스템에서는 상태 변경이 DB에 반영되면 그만인 구조다. 주문 생성, 회원 가입, 결제 완료 등 중요한 도메인 이벤트가 발생하더라도, 그 사실이 시스템 외부에 드러나지 않는다. 이는 확장성과 통합성에서 큰 약점이 된다. 이벤트 기반 아키텍처에서는 상태 변경이 일어나면 반드시 이벤트가 생성되고, 이 이벤트는 다른 시스템 또는 모듈이 반응할 수 있도록 발행되어야 한다. 그러나 전통적 구조에서는 이벤트는 코드 내에서 로그로만 남거나, 단순한 콜백 함수로 끝나버리는 경우가 많다. 이벤트 소싱(Event Sourcing)은 이런 문제를 정면으로 해결하는 방법론이다. 상태 변경이 아닌 변경의 이벤트 자체를 저장하고, 이를 기준으로 시스템을 구성하는 패턴이다. 이는 CQRS와 매우 자연스럽게 결합된다.
2. 이벤트 소싱의 철학과 이점
2-1. 상태가 아니라 이벤트를 저장한다는 패러다임 전환
이벤트 소싱(Event Sourcing)은 전통적인 상태 기반 저장 방식과 정반대의 개념에서 출발한다. 기존 시스템은 객체의 “최종 상태”만을 저장한다. 예를 들어 주문 객체(Order)는 생성일, 상태, 금액 등을 테이블에 저장하며, 이력은 별도의 로그 테이블이 있어야만 추적할 수 있다. 하지만 이벤트 소싱은 그 반대다. 객체의 상태는 저장하지 않고, 그 상태를 만들어낸 사건(Event)의 연속만을 저장한다.
이러한 전환은 단순히 저장 방식의 변화가 아니라, 도메인 모델에 대한 사고 방식 전체를 바꾸는 일이다. 객체는 현재 상태를 직접 저장하지 않고, 여러 개의 도메인 이벤트를 재생(replay)함으로써 현재 상태를 유도한다. 예를 들어 "OrderCreated → ItemAdded → OrderPaid" 같은 이벤트 흐름이 하나의 주문 상태를 구성하게 되는 것이다. 이 방식은 변경 이력을 자동으로 기록하고, 그 자체가 데이터의 진실(Truth)로 간주된다. 즉, “지금 이 객체가 어떤 상태인가?”가 아니라 “이 객체는 어떻게 이런 상태에 도달했는가?”를 저장하는 방식으로, 시스템의 투명성과 분석 가능성을 획기적으로 끌어올린다.
2-2. 감사(Audit) 로그를 넘어선 실질적 이력 보존
이벤트 소싱은 단순한 로깅 이상의 가치를 제공한다. 기존 시스템에서의 감사(Audit) 로그는 보통 2차적 정보이며, 실제 도메인 모델과는 분리되어 있다. 하지만 이벤트 소싱은 애초에 도메인 이벤트가 모델을 구성하는 핵심 요소이기 때문에, 이벤트 자체가 시스템의 근간이 된다.
예를 들어 "고객 등급이 실버에서 골드로 바뀌었다"는 이벤트가 있다면, 이는 단순히 상태가 바뀐 것이 아니라 어떤 조건이 충족되어 등급 변경 이벤트가 발생한 것이다. 이러한 의미 있는 정보는 나중에 원인 분석, 규칙 리디자인, 머신러닝 학습 데이터로도 활용될 수 있다.
또한 이벤트 기반 기록은 과거 특정 시점의 상태를 재현하는 기능(time travel) 을 가능하게 한다. “이 사용자는 6개월 전엔 어떤 상태였지?” “그때 이 주문은 왜 실패했지?” 같은 질문에 대해 DB 스냅샷이 아니라 이벤트 재생을 통해 정확히 재현할 수 있다. 이는 디버깅, 회귀 테스트, 사고 분석에 있어서 기존 시스템과 비교할 수 없는 수준의 가시성을 제공한다.
2-3. 비동기 이벤트 처리와 자연스러운 확장성 확보
이벤트 소싱의 또 다른 강점은 비동기 아키텍처와의 궁합이다. 시스템에서 발생하는 이벤트는 메시지 브로커(Kafka, RabbitMQ 등)를 통해 다양한 서브시스템으로 전달될 수 있으며, 이는 시스템 간 결합도를 낮추고 확장성을 확보하는 핵심 기반이 된다.
예를 들어 "결제 완료됨(PaymentCompleted)" 이벤트가 발행되면, 이를 구독하는 배송 서비스는 배송 프로세스를 시작할 수 있고, 마케팅 서비스는 적립 포인트를 계산할 수 있다. 각 서비스는 이 이벤트에 관심이 있을 뿐, 발신자가 누구인지 알 필요도 없고, 동기 호출도 필요 없다.
이러한 구조는 마이크로서비스나 이벤트 드리븐 아키텍처에서 필수적인 요소이며, CQRS 구조와 함께 쓰일 때 가장 큰 시너지를 낸다. 명령은 이벤트를 기록하고, 조회는 이벤트의 결과를 재가공한 읽기 모델에서 빠르게 제공하므로, 두 요구사항을 완전히 분리된 방식으로 최적화할 수 있게 된다.
2-4. 복구 가능성과 회복 탄력성
이벤트 소싱은 시스템 장애나 데이터 손실 발생 시에도 복구 가능성(recoverability) 을 극대화한다. 일반적인 CRUD 기반 시스템은 테이블 데이터가 손상되거나 중간 상태에서 문제가 생기면 복구가 쉽지 않다. 반면 이벤트 소싱은 전체 변경 이력이 누적되어 있으므로, 특정 시점까지의 상태를 재생성해 복구할 수 있다.
예를 들어 주문 데이터베이스가 손상되었더라도, "OrderCreated → ItemAdded → OrderPaid"라는 이벤트 스트림이 존재한다면, 해당 주문 객체를 복원할 수 있다. 이는 시스템 전체의 탄력성(resilience) 을 높여주며, 롤백 전략보다 훨씬 강력한 복구 전략이 된다. 또한 불완전한 이벤트 처리 도중 시스템이 중단되더라도, 메시지 브로커를 통해 이벤트가 다시 전달되고 재시도되므로 데이터 유실 없이 처리를 이어갈 수 있다. 이러한 회복력은 이벤트 소싱을 도입하는 가장 실용적인 이유 중 하나로 꼽힌다.
3. CQRS란 무엇이며 왜 필요한가
3-1. 명령과 조회의 개념적 분리
CQRS(Command Query Responsibility Segregation)는 말 그대로 “명령과 조회의 책임을 분리한다”는 설계 원칙이다. 전통적인 CRUD 모델에서는 도메인 객체 하나가 Create, Read, Update, Delete를 모두 처리하지만, CQRS에서는 이들을 명확히 분리해서 설계한다. Command는 상태 변경, Query는 데이터 조회만 담당하게 되며, 이 둘은 서로 다른 모델로 구현될 수 있다.
이 분리는 단순한 코드 정리 수준이 아니라, 설계 철학의 변화를 의미한다. 명령은 비즈니스 규칙을 충실히 반영하고 트랜잭션 제어를 정확히 해야 하며, 정합성을 중요하게 여긴다. 반면, 조회는 성능, 응답 속도, 사용자 경험에 집중해야 하므로 집계, 필터링, 정렬 등 다양한 기능이 요구된다. 이처럼 성격이 전혀 다른 두 책임을 분리하는 것이 CQRS의 본질이다. CQRS는 결국 시스템의 복잡도를 역할 단위로 나누는 것이다. 특히 도메인 복잡도는 높지만, 조회 요구사항이 다양하고 빠른 응답이 필요한 시스템에서는 이 분리가 복잡성을 줄이고 유지보수를 쉽게 해주는 핵심 전략이 된다.
3-2. 데이터 저장 모델의 분리 가능성
CQRS의 또 다른 강력한 특징은 쓰기 모델과 읽기 모델의 데이터 저장소를 분리할 수 있다는 점이다. 즉, 명령(Command) 모델은 RDB를 사용하고, 조회(Query) 모델은 Elasticsearch나 Redis, MongoDB 같은 NoSQL을 사용하는 등 서로 다른 기술 스택을 선택할 수 있다.
이는 단순한 기술 장점을 넘어, 실제 성능과 설계 유연성에 큰 영향을 준다. 쓰기 모델은 정합성과 트랜잭션 관리가 중요하기 때문에 정규화된 관계형 DB가 적합하지만, 조회는 빠른 응답과 필터링, 집계가 핵심이므로 비정규화된 구조나 검색 최적화된 저장소가 더 효과적이다. CQRS는 이런 요구사항을 완전히 충족시킨다.
또한 이 구조는 데이터베이스 락, 동시성 이슈, 과도한 조인 문제 등을 완화시키며, 각 모델이 자신의 책임에 맞게 최적화된 저장 방식과 스키마를 선택할 수 있다. 결과적으로 시스템의 전체적인 성능과 확장성이 크게 향상된다.
3-3. 복잡한 읽기 요구사항에 최적화
현대 웹 애플리케이션은 다양한 형태의 데이터를 실시간으로 제공해야 한다. 예를 들어 대시보드에는 수십 개의 지표, 로그 히스토리, 사용자 활동 내역 등이 한눈에 보여야 하며, 각각은 필터링, 정렬, 검색이 가능해야 한다. 이러한 요구는 도메인 모델 기반의 단일 조회 모델로는 만족시키기 어렵다.
CQRS에서는 읽기 모델을 자유롭게 구성할 수 있다. 예를 들어 OrderReadModel은 사용자가 주문한 상품명, 총합계, 주문 상태 등만 갖는 단순 구조로 만들 수 있고, 이는 비정규화된 테이블이나 NoSQL 문서 형태로 저장된다. 또한 실시간 데이터 캐싱 구조와 결합해 Redis, In-memory DB, View DB 등으로 분산 처리할 수 있다. 이 구조는 조회 성능을 획기적으로 향상시키며, 사용자 경험(UX)을 개선하고 서버 부하를 줄이는 핵심 전략이 된다. 읽기 모델은 별도 구성되기 때문에, 도메인 로직을 침해하지 않고도 다양한 화면 요구사항을 유연하게 대응할 수 있다.
3-4. 명령 모델을 도메인 중심으로 유지할 수 있음
CQRS는 단순히 조회를 분리하기 위한 전략이 아니라, 쓰기 모델이 도메인 중심으로 설계될 수 있도록 도와주는 구조다. 명령 모델은 복잡한 비즈니스 로직, 애그리거트, 도메인 서비스 등 도메인 주도 설계의 원칙을 충분히 반영할 수 있다. 전통적인 CRUD에서는 “조회가 편하게” 설계하다 보면 엔터티 구조나 도메인 모델이 왜곡되기 쉬운데, CQRS에서는 읽기 모델은 전용 View Model로 분리되기 때문에 도메인 객체가 오직 비즈니스 규칙과 상태 변화에만 집중할 수 있게 된다. 이로써 객체 간 책임이 명확해지고, 테스트 가능한 구조로 설계할 수 있다.
또한, 명령 모델에서는 비즈니스 트랜잭션의 정합성 유지가 핵심이므로, 하나의 애그리거트 안에서만 트랜잭션을 처리하고, 변경 결과는 이벤트로 발행하여 다른 서브시스템이 비동기로 반응하게 하면 된다. 이는 CQRS와 이벤트 소싱이 자연스럽게 결합되는 지점이기도 하다.
4. 이벤트 소싱과 CQRS의 통합 설계 전략
4-1. 명령 → 이벤트 → 읽기 모델 갱신 흐름
이벤트 소싱과 CQRS를 함께 적용한 시스템은 일반적인 CRUD 방식과 매우 다른 요청-응답 흐름을 가진다. 클라이언트가 명령(Command)을 실행하면, 그에 대응하는 도메인 로직이 수행되고, 상태를 직접 수정하는 대신 해당 도메인 이벤트를 생성하고 저장한다. 이 이벤트는 Event Store에 기록되며, 이는 시스템에서 발생한 '사실'을 영구적으로 보존하는 원천이 된다.
이후 이 이벤트는 메시지 브로커나 이벤트 버스를 통해 구독자에게 전파된다. 읽기 모델을 담당하는 Projection 또는 View Updater는 해당 이벤트를 받아 적절한 형태의 읽기 저장소를 갱신한다. 예를 들어 OrderCreated 이벤트가 발생하면, OrderSummaryViewUpdater가 이 이벤트를 받아 Redis나 Elasticsearch에 주문 요약 정보를 업데이트하는 식이다.
이 구조는 상태 변경과 조회 데이터 갱신을 완전히 분리함으로써, 도메인 로직의 일관성과 조회 성능을 모두 확보할 수 있게 해준다. 단일 요청이 다양한 후속 작업으로 이어질 수 있으며, 각 이벤트는 다수의 구독자에게 영향을 줄 수 있기 때문에 확장성과 유연성 면에서 매우 뛰어난 설계다.
4-2. 이벤트 핸들러와 프로젝션 설계 전략
이벤트 소싱 기반 시스템에서 이벤트 핸들러는 단순한 트리거가 아니다. 핸들러는 이벤트를 받아 읽기 모델을 생성하거나 갱신하는 핵심 컴포넌트이며, 때로는 여러 핸들러가 하나의 이벤트에 반응하기도 한다. 중요한 것은 이들이 절대 상태를 변경하지 않고, 오직 읽기 모델을 최신화하는 책임만을 가진다는 점이다.
Projection 컴포넌트는 각 이벤트에 따라 적절한 View Model을 업데이트한다. 예를 들어 UserCreated, UserDeactivated, UserUpdated 같은 이벤트를 통해 사용자 요약 뷰를 갱신할 수 있다. 이때 View는 일반적으로 비정규화되고, 조회 최적화를 위한 별도의 DB 테이블이나 NoSQL 문서로 저장된다.
핸들러 설계 시 주의할 점은 이벤트 순서 보장, 중복 처리 방지, 처리 실패에 대한 리트라이 설계다. 메시지 큐(Kafka, RabbitMQ) 기반으로 이벤트를 구독할 경우, 재처리와 순서 유지를 위한 토픽 설계, 오프셋 관리, 역직렬화 구조까지 꼼꼼히 구성해야 한다. 제대로 설계된 프로젝션 구조는 CQRS의 조회 성능을 극대화시키는 핵심 기반이 된다.
4-3. 일관성 보장의 방식 – 최종 일관성 전략
이벤트 소싱 + CQRS 환경에서 가장 중요한 개념 중 하나는 즉시 일관성(strong consistency) 이 아닌 최종 일관성(eventual consistency) 이다. 즉, 명령을 통해 이벤트가 발생한 직후에는 읽기 모델이 아직 갱신되지 않았을 수 있으며, 몇 초의 지연 이후에야 반영되는 것이 정상이다.
이는 사용자 경험과 시스템 설계 모두에 영향을 미친다. 예를 들어 사용자가 어떤 주문을 완료하고 "내 주문 내역"을 즉시 조회했을 때, 아직 읽기 모델이 업데이트되지 않아 해당 주문이 보이지 않을 수 있다. 이러한 상황을 처리하기 위해선 클라이언트에서 “처리 중입니다” 메시지를 표시하거나, 서버에서 캐시된 상태로 일시적으로 처리하는 등의 설계가 필요하다.
중요한 것은 이 일관성 지연이 의도된 설계라는 점이다. CQRS + 이벤트 소싱 구조는 빠른 처리와 확장성을 위해 일관성을 비동기로 맞추는 것을 감수하는 구조다. 비즈니스 요구에 따라 일부 민감한 데이터는 Command 모델에서 직접 상태를 리턴하거나, 읽기 모델을 별도로 보강할 수 있지만, 기본 전략은 분산 환경에서의 유연한 일관성 확보에 맞춰져야 한다.
4-4. 동기화 실패와 복구 전략
이벤트 소싱과 CQRS 시스템은 복잡한 구조를 가지기 때문에, 다양한 실패 상황을 고려한 복구 전략이 반드시 필요하다. 특히 이벤트 소비 실패, 네트워크 지연, 메시지 중복 처리, 순서 꼬임 등은 시스템 전체 일관성을 해칠 수 있는 중요한 리스크다.
이러한 문제를 방지하기 위해 이벤트 핸들러는 항상 멱등성(idempotency) 을 고려해야 한다. 즉, 동일한 이벤트가 두 번 전달되더라도 같은 결과만 발생하도록 설계해야 한다. 이를 위해 이벤트 ID를 기준으로 처리 여부를 기록하거나, View Model의 상태 자체가 변화가 없을 경우 무시되도록 구성하는 방식이 일반적이다.
또한 View Model과 Event Store 간의 오프셋 기반 리플레이 기능이 중요하다. 만약 읽기 모델이 손상되거나 지연된 경우, 저장된 이벤트 로그를 다시 재생하여 전체 읽기 모델을 재구성할 수 있어야 한다. 이 기능은 이벤트 소싱의 가장 큰 장점 중 하나이며, 장애 복구나 로직 수정 시 전체 리플레이를 통해 시스템 일관성을 빠르게 회복할 수 있게 해준다.
5. 실무 적용 시 주의사항과 팁
5-1. 도입 전 “왜 필요한가”를 명확히 정의하라
이벤트 소싱과 CQRS는 강력한 설계 패턴이지만, 도입 자체가 목적이 되어선 안 된다. 먼저 반드시 고민해야 할 질문은 “이 시스템이 CQRS와 이벤트 소싱이 정말 필요한 수준인가?” 이다. 단순한 CRUD 시스템이나, 명령과 조회의 구분이 명확하지 않은 프로젝트에 무리하게 적용하면 오히려 복잡도와 유지비용만 증가한다.
도입 전 명확히 해야 할 것은 현재 시스템의 문제점이다. 예를 들어 “조회 성능이 병목이다”, “이벤트 기반 확장이 필요하다”, “도메인 상태 변경 이력을 추적해야 한다” 같은 요구사항이 분명해야 한다. 이러한 니즈 없이 단순히 “트렌드니까” 도입하는 경우, 팀 전체가 복잡한 아키텍처를 감당하느라 오히려 생산성이 떨어지는 결과를 낳는다. 또한 도입 초기에 작은 모듈부터 점진적으로 적용해보는 것이 좋다. 예를 들어 이벤트 소싱은 주문(Order), 송금(Transaction) 등 상태 이력이 중요한 모듈부터 도입하고, CQRS는 관리자 대시보드나 고부하 조회 API부터 적용하는 식으로 단계별 확장 전략을 택해야 한다.
5-2. 개발자, 기획자, 운영자의 공통 이해가 필요하다
CQRS와 이벤트 소싱은 설계 패턴이지만, 동시에 조직의 의사소통 방식까지 바꾸는 구조다. 명령과 조회가 분리되면 API 스펙도 나뉘고, 읽기 모델이 실시간 반영되지 않기 때문에 기획자나 QA는 그 차이를 명확히 이해하고 있어야 한다. “왜 바로 반영되지 않지?”, “왜 이 API는 응답이 느리지?” 같은 오해를 방지하려면 아키텍처 수준의 이해 공유가 필요하다. 또한 운영 환경에서도 이벤트 흐름, 메시지 큐 상태, 프로젝션 실패 등에 대한 모니터링 체계가 중요하다. 로그 기반의 상태 추적, 이벤트 처리 실패 알림, 리플레이 도구의 구축까지 포함한 운영 툴링 체계를 함께 설계해야 한다. 특히 이벤트가 누락되면 조회 데이터가 유실되는 구조이기 때문에, 장애 발생 시 빠르게 원인을 진단할 수 있는 관찰 가능성(observability)이 필수다.
즉, 단순히 개발자만 설계를 이해하는 것이 아니라, 기획자, QA, 운영자까지 포함한 전체 협업 프로세스가 CQRS와 이벤트 중심으로 정렬되어야 한다. 이를 위한 문서화, 가이드 작성, 테스트 시나리오 정의도 중요한 준비 과정이다.
5-3. 테스트 전략을 설계에 포함시켜라
CQRS + 이벤트 소싱은 테스트 전략까지 설계에 포함하지 않으면 빠르게 유지보수가 어려운 구조로 변질된다. 먼저 명령(Command)에 대한 테스트는 명확한 도메인 이벤트가 발생했는지를 검증해야 한다. 즉, 어떤 Command가 주어졌을 때 “무슨 이벤트가 발생했는가”를 테스트해야 하고, 상태 검증이 아닌 이벤트 결과에 집중해야 한다.
반대로 읽기 모델은 Projection의 정확성과 이벤트 재생 테스트가 핵심이다. 이벤트 핸들러가 특정 View Model을 예상한 형태로 갱신하는지, 중복 이벤트나 순서 꼬임이 발생했을 때도 문제없이 작동하는지를 자동화된 테스트로 검증해야 한다. 특히 읽기 모델은 여러 이벤트에 반응하므로 **시나리오 기반 테스트(Scenario Test)**를 통해 전체 흐름이 유효한지를 확인해야 한다.
추가로 Event Store에 저장된 이벤트의 스키마가 변경될 경우를 대비해, 이벤트 버전 관리 및 마이그레이션 도구도 함께 고려해야 한다. “과거 이벤트를 어떻게 처리할 것인가?”, “이벤트 스키마가 바뀌면 프로젝션은 어떻게 리플레이할 것인가?” 같은 질문에 대한 전략이 없다면, 시스템은 점점 리스크를 품은 상태로 누적되기 쉽다.
5-4. 무분별한 적용을 피하고, 조합을 유연하게 설계하라
이벤트 소싱과 CQRS는 궁합이 좋은 구조이지만, 반드시 함께 도입해야 하는 건 아니다. 예를 들어 “조회는 다양하지만 상태 변경은 단순한” 시스템이라면 CQRS만 도입하고 이벤트 소싱은 생략하는 것이 효율적일 수 있다. 반대로 “이력 보존이 핵심인 도메인”이라면 이벤트 소싱만 도입하고 명령-조회는 단일 모델로 유지하는 것도 가능하다.
즉, 설계자는 두 가지를 조합 가능한 도구로 봐야지, 절대적인 설계 원칙처럼 받아들여선 안 된다. 모든 모듈을 CQRS로 만들고 모든 상태를 이벤트 소싱으로 처리하면, 시스템은 지나치게 복잡해지고 팀원들이 코드를 따라가기 어려워진다. 특히 규모가 작은 스타트업이나 MVP 단계에서는 오히려 역효과가 날 수 있다.
또한 반드시 하나의 아키텍처만을 고집하지 말고, 도메인 특성에 따라 혼합 구조를 선택하는 유연성이 필요하다. 일부 모듈은 이벤트 소싱 + CQRS, 일부는 단순한 CRUD + Cache, 또 다른 곳은 복합 쿼리용 View 모델만 따로 구성하는 방식으로 혼용해도 괜찮다. 중요한 것은 아키텍처의 순수성이 아니라 비즈니스 목표에 맞는 합리적인 균형이다.
'컴퓨터공학' 카테고리의 다른 글
애그리거트와 리포지토리 패턴 (0) | 2025.05.24 |
---|---|
Value Object vs Entity (0) | 2025.05.24 |
도메인 주도 설계(DDD) 핵심 (0) | 2025.05.23 |
전략, 옵저버, 커맨드 패턴 (0) | 2025.05.23 |
싱글턴과 DI 컨테이너 구조 (0) | 2025.05.23 |