CQRS는 Command and Query Responsibility Segregation(명령과 조회의 책임 분리)를 나타낸다. CQRS는 시스템에서 명령을 처리하는 책임과 조회를 처리하는 책임을 분리하는 것이 핵심이다.
- 명령은 시스템의 상태를 변경하는 작업을 의미
- 조회는 시스템의 상태를 반환하는 작업을 의미
정리하면, CQRS는 시스템의 상태를 변경하는 작업과 시스템의 상태를 반환하는 작업의 책임을 분리하는 것이다.
일반적인 애플리케이션은 쿼리와 업데이트에 동일한 데이터 모델을 사용한다.
간단한 애플리케이션에는 이것이 적합하지만 애플리케이션이 복잡해질수록 위 방법은 어려워진다. 왜냐하면 애플리케이션은 읽기 쪽에서 다른 쿼리를 실행할 수 있고, 그러면 모양이 다른 DTO를 반환하기 때문에 개체 매핑이 복잡해지기 때문이다. 또한 동일한 데이터 집합에서 작업을 병렬로 수행할 때 데이터 경합이 발생할 수 있으며, 데이터 저장소 및 데이터 액세스 계층에 대한 로드 및 정보를 검색하는데 필요한 쿼리의 복잡성으로 성능에 부정적인 영향을 미칠 수 있다. 그리고 각 엔터티는 읽기 및 쓰기 작업의 대상이 되므로 잘못된 컨텍스트에서 데이터를 노출할 수 있으므로 보안 및 사용 권한 관리가 복잡해질 수 있다.
반면 CQRS는 명령 및 쿼리의 책임을 분리하는 패턴으로, CQRS를 구현하면 성능, 확장성 보안을 극대화할 수 있다.
이 패턴에서 명령은 데이터 중심이 아닌 작업을 기반으로 해야하며, 비동기 처리를 위해 큐에 배치될 수도 있다. 또한 쿼리는 데이터베이스를 수정하지 않으며, 쿼리는 도메인 정보를 캡슐화지 않는 DTO를 반환한다.
읽기 저장소는 쓰기 저장소의 읽기 전용 복제본이거나 읽기 및 쓰기 저장소가 전혀 다른 구조일 수도 있다. 여러 일긱 전용 복제본을 사용하며 쿼리의 성능이 당연히 향상될 수 있으며, 부하를 감안하여 각 저장소를 적절하게 확장할 수도 있다.
메모리 내 모델은 동일한 데이터베이스를 공유할 수 있으며, 이 경우 데이터베이스는 두 모델 간의 통신 역할을 한다. 위와 같이 여러 형태로 변형을 하여 CQRS를 적용할 수 있다. 별도의 데이터베이스를 허용하며 쿼리측 데이터베이스를 실시간 리포팅 데이터베이스(Reporting Database)로 만들 수도 있다. 이 경우 두 모델 또는 데이터베이스 간에 통신 매커니즘이 추가되어야 한다.
CQRS를 사용해야 하는 경우는 아래와 같다.
- 많은 사용자가 동일한 데이터에 병렬로 액세스하는 공동작업 도메인일 경우
- 개발자 중 한 팀은 쓰기 모델에 포함되는 복잡한 도메인 모델에 집중하고 다른 한 팀은 읽기 모델과 사용자 인터페이스에 집중할 수 있는 경우
- 시스템이 시간이 지나면서 진화할 것으로 예상되어 여러 버전의 모델을 포함할 수 있거나 비즈니스 규칙이 저기적으로 변하는 경우
- 가장 가치있는 시스템의 제한된 구역에 CQRS 적용을 고려해야 한다. -> 도메인 또는 비즈니스 규칙이 간단하거나 간단한 CRUD 스타일의 사용자 인터페이스와 데이터 액세스 작업만으로 충분하다면 CQRS는 어울리지 않는다. 모든 상황에서 최상단에 패턴으로 CQRS를 위치하는 건 어디에서도 추천하지 않는 방법이다. -> DDD 용어에서 말하는 시스템 전체가 아닌 시스템의 특정 부분(Bounded Context)에서만 사용해야한다.
- CQRS는 이벤트 기간 프로그래밍 모델에 적합하다. -> CQRS 시스템이 이벤트 콜라보레이션(Event Collaboration)과 통신하는 별도의 서비스로 분리되는 것이 일반적이며, 이를 통해 이벤트 소싱(Event Sourcing)을 쉽게 이용할 수 있다.
: 이벤트 전체를 하나의 데이터로 저장하는 방식


