MSA에서는 다음 두 가지 패턴으로 쿼리를 구현한다.
- API 조합 패턴: 클라이언트가 데이터를 가진 여러 서비스를 직접 호출해 그 결과를 조합하는 패턴
- CQRS 패턴 (커맨드 쿼리 책임 분리): 쿼리만 지원하는 하나 이상의 뷰 전용 DB를 유지하는 패턴
7.1 API 조합 패턴 응용 쿼리
API 조합 패턴 개요

- API 조합기: 프로바이더 서비스를 쿼리해 데이터 조회
- 프로바이더 서비스: 최종 결과로 반환할 데이터의 일부를 갖고 있는 서비스
애그리거트가 거대한 데이터 뭉치를 비효율적으로 인메모리 조인을 해야 할 수도 있다!
API 조합 설계 이슈
어느 컴포넌트를 쿼리 작업의 API 조합기로 선정할 것인가?
- 서비스 클라이언트 내에 위치
- 애플리케이션의 외부 API가 구현된 API 게이트웨이
- 스탠드 얼론 서비스
어떻게 해야 효율적으로 취합 로직을 작성할 것인가?
리액티브 프로그래밍 모델 사용해야 한다. 순서가 필요하면 일부 순차 호출
API 조합 패턴의 장단점
- 오버헤드 증가: 여러 번 요청하고 여러 DB 쿼리 실행해야하니까
- 가용성 저하 우려: 하나의 서비스로 처리하는 것에 비해 가용성 낮아진다.
- 데이터 캐싱, 미완성 데이터 반환 방식으로 가용성을 높일 수 있다.
- 데이터 일관성 결여
7.2 CQRS 패턴
- 트랜잭션: RDBMS
- 텍스트 검색 쿼리: Elastic Search 등 텍스트 검색 DB
이런 종류의 아키텍처를 일반화 함
CQRS의 필요성
구현하기 어려운 다중 서비스 쿼리 예시
Filter 할 때 매치할 keywords
속성이 있는데 해당 속성이 모든 서비스에 존재 하지 않는 경우
-> API 조합기로 인메모리 조인하거나 / 먼저 조회하고 다른 서비스에 조회하고.. 어쩌구
-> 거대한 데이터를 가져옴 / 과도한 네트워크 트래픽 유발
어려운 단일 서비스 쿼리 예시
- 데이터를 가진 서비스에 쿼리를 구현하는 게 부적절한 경우: 음식점 정보 제공하는 서비스에서 주어진 위치로 배달 가능한 음식점 검색하도록 하면...
- 서비스 DB가 효율적인 쿼리를 지원하지 않는 경우: DB에서 지리 공간 기능을 지원하지 않는 경우
CQRS의 개요
결론은 ..
- 비효율적인 인 메모리 조인 필요
- 서비스 DB가 효율적인 쿼리를 지원하지 않는 경우
- 관심사 분리 필요
이러한 문제가 있는데, 이걸 해결할 수 있는 묘안이 CQRS 패턴이다.
CQRS는 커맨드, 쿼리 분리

- 커맨드: CUD
- 쿼리: R, 지원해야 하는 쿼리에 대해 모든 종류의 DB 지원/ 도메인 이벤트 구독 후 DB 업데이트
CQRS의 장점
- MSA에서 쿼리를 효율적으로 구현
- 다양한 쿼리를 효율적으로 구현: 각 쿼리가 효율적으로 구현된 하나 이상의 뷰를 정의해 단일 데이터 저장소 한계 극복
- 이벤트 소싱 애플리케이션에서 쿼리 가능: 기본키 쿼리만 지원하는 한계 극복
- 관심사 더 분리
CQRS의 단점
- 아키텍처 복잡
- 복제 시차 처리 -> 모바일에서는 로컬 모델 업데이트해서 시차 해소한다..!
7.3 CQRS 뷰 설계
- 이벤트 핸들러: 이벤트 구독해서 DB 업데이트
- 쿼리 API: 데이터 조회
뷰 DB 선택
1) SQL vs NoSQL
- RDBMS
- NoSQL DB: 트랜잭션 기능 제한적이지만 유연성/확장성에 유리 -> CQRS 뷰와 잘 맞는 편
2) 업데이트 작업 지원
이벤트 핸들러에서 기본키로 수정/삭제 할테지만 외래키를 사용하는 경우도 존재한다.
- RDBMS, MongoDB 는 필요한 칼럼 인덱스 생성하면 됨
- 다른 NoSQL DB는 비기본키 기반으로 업데이트하기가 쉽지 않다.
- DynamoDB는 기본키 기반의 수정/삭제만 지원되기에 보조 인덱스를 쿼리해서 수정/삭제할 항목의 기본키 결정해야한다.
데이터 접근 모듈 설계
동시성 처리
뷰가 여러 종류의 애그리거트가 발행한 이벤트를 구독한다면 동일한 레코드에 대해 동시 업데이트할 수 있다.
멱등한 이벤트 핸들러
같은 이벤트를 여러 번 받을 수 있다. 이때 처리 후 결과가 완전히 동일하면 문제없지만 처리할 때마다 달라진다면 문제가 발생한다.
- 이벤트 ID 로 중복확인
[애그리거트타입, 애그리거트ID] -> max(eventId)
이렇게 담고 있으면 됨
클라이언트 애플리케이션이 최종 일관된 뷰를 사용할 수 있다
1. 커맨드 작업 후 이벤트 ID가 포함된 토큰 반환
2. 이 토큰을 쿼리 작업에 전달
해당 이벤트에 의해 뷰가 업데이트되지 않는다면 에러 반환 -> 중복 이벤트 감지 메커니즘을 뷰 모듈에 구현 가능
CQRS 뷰 추가 및 업데이트
아카이빙된 이벤트 이용해 CQRS 뷰 구축
메시지 브로커가 메시지를 무기한 보관할 수 없으므로 AWS S3처럼 아카이빙 필요
CQRS 뷰를 단계적으로 구축
- 주기적으로 각 애그리거트 인스턴스의 스냅샷을 그 이전의 스냅샷과 이 스냅샷이 생성된 이후 쭉 발생한 이벤트 바탕으로 계산
- 계산된 스냅샷과 그 이후 발생한 이벤트 이용해서 뷰 생성
7.4 CQRS 뷰 구현: AWS DynamoDB 응용
DynamoDB: NoSQL, 이름-값 쌍
EventHandler
DynamoDB 데이터 모델링 및 쿼리 설계
- 테이블 설계: 기본키로 삽입/수정/조회, items 처럼 리스트로 저장되는 건 맵 리스트 형태로 저장됨
- 쿼리 전용 인덱스
- 파티션 키: Z축 확장할 때 이 키를 보고 저장소 파티션 선택
- 정렬 키: 해당 키로 정렬
- 중복 이벤트 감지:
UpdateItem()
(개별 아이템 업뎃, 필요시 생성) 메커니즘 이용해 중복 이벤트가 아닐 때만 아이템 업데이트
- <애그리거트타입애그리거트ID>:이벤트ID 저장한 후 새로 들어온 이벤트 ID가 작거나 같으면 중복이라고 판단
DataAccess
- DynamoDB 테이블 및 관련 헬퍼 클래스를 조회/수정하는 메서드가 정의된 DAO 포함