이전 설계 2편에 이어 해당 편은 도입 검토중인 CQRS에 대해서 알아본 것을 정리하고 어떤 형태로 적용할지 결정하려 한다.
CQRS : Command Query Responsibility Segregation
명령(시스템 데이터 변경) 역할을 수행하는 구성 요소와 쿼리(시스템 데이터 조회) 역할을 수행하는 구성 요소를 나누는 것.
명령
- 시스템 데이터 변경
- 기능 : 주문취소, 배송완료 등
쿼리
RS : Responsibility Segregation
책임
분리
CQRS가 필요한 경우
명령과 조회에 단일 모델을 사용하면?

단점
- 코드 역할/책임 모호
- 의미/가독성 등 나빠짐
- 유지보수성이 떨어짐

- 즉시/지연 로딩 적용 기준 모호해짐
단점이 발생하는 이유
- 명령과 쿼리는 다루는 데이터가 다름
- 명령 : 한 영역의 데이터
- 쿼리 : 여러 영역의 데이터

- 명령과 쿼리는 코드 변경 빈도, 사용자 다름
- 예시 : 백오피스의 주문 목록 조회 기능, 사용자의 주문 기능
- 변경 빈도가 다른 기능이 한 코드에 있으면
- 서로 다른 이유로 코드가 바뀌고
- 이는 곧 책임의 크기가 적당하지 않다는 것
- 기능마다 성능 요구가 다름
- 기능마다 트래픽 패턴, 성능 요구 다름
- 사용자 상품 목록 조회, 상품 상세 조회
- 댓글 등록
- 주문
- 백오피스 판매 수치 등
- 기능마다 서로 다른 성능 향상 방법 필요
- 단일 모델로는 다양한 성능 향상 기법 적용이 어려울 수 있음
구현 형태 예시

1. 같은 프로세스, 같은 DB

- 가장 단순
- 명령/쿼리 동일 데이터 보장
- 코드 수준에서만 분리
2. 같은 프로세스, 같은 DB, 다른 테이블

- 쿼리 전용 테이블 사용
- 명령 수행 시 쿼리 전용 테이블 까지 변경함
3. 같은 프로세스, 다른 DB

- 명령 수행 후 변경 내역을 쿼리 DB로 전파함
4. 다른 프로세스, 다른 DB

- 3번과 동일하나 다른 프로세스에서 진행
- MSA에서 주로 사용되는 방식
다른 DB로 변경 전파

1. 명령이 직접 쿼리 DB를 수정하는 방식
- 카프카같은 메시징 큐로 전달하는 변형도 있음
- 장점 : 구현이 단순
- 단점 : 데이터 유실 가능성이 있음
- 쿼리 DB나 메시징이 일시적으로 문제 발생하게 되면, 쿼리 DB에 반영되야 할 데이터가 유실되거나, 쿼리 DB나 메시징의 문제때문에 명령 수행 기능 자체가 에러 날 수 있음
2. 변경 내역을 기록하고, 별도 전파기를 이용해서 내역을 전달하는 방식
- 1번과 마찬가지로 중간에 메시징 큐를 두는 변형도 있음
- 장점 : 변경을 진행하고 그 내역을 별도 테이블에 저장함, 이 과정은 한 트랙잭션으로 처리되기때문에 변경 내역이 유실되지 않음
- 단점 : 전파기를 따로 구현해야됨
3. DB가 제공하는 CDC 이용
- 메시징 큐 변형 존재
- DB 바이너리 로그를 읽어서 데이터를 확인하고, 그 데이터를 쿼리 DB에 전달
- 장점 : 2번과 비슷하나 명령쪽에서 내역을 따로 저장하지 않아도 되니, 코드가 단순해진다.
다른 DB 사용 시 주의 사항
- 데이터 유실
- 유실 허용 여부에 따라 DB 트랜잭션 범위 중요
- 예 : 주문 목록은 유실되면 절대 안됨, 최근 인기글은 일시적으로 안되도 치명적이진 않음
- 허용 가능 지연 시간
- 명령의 변경 내역을 얼마나 빨리 쿼리쪽에 반영시켜야하는가에 따라 구현 선택이 달라질 수 있음
- 중복 전달
- 쿼리쪽 DB에 변경 데이터를 전달하는 과정에서, 문제가 생기면 다시 전달할 수 있는 수단이 필요, 그 과정에서 쿼리쪽에 이미 반영된 데이터를 중복 전달하는 경우도 발생할 것.
- 중복 전달하더라도 쿼리쪽 데이터가 망가지지않도록 별도 처리가 필요함
출처 : https://martinfowler.com/bliki/CQRS.html
https://www.youtube.com/watch?v=H1IF3BUeFb8
https://www.youtube.com/watch?v=xf0kXMTFJm8