Blog

도메인 주도 개발 시작하기(11)

도메인 주도 개발 시작하기(11)

11-1. 단일 모델의 단점

단일 모델로 데이터를 처리하게 될 경우 다양한 정보를 보여줘야할 때 여러개의 애그리거트들을 부르거나 네이티브 쿼리를 사용해야 할 수도 있다
시스템 상태를 변경할 때와 조회할 때 단일 도메인 모델을 사용하기 때문이다
예시
객체 지향으로 도메인 모델을 구현할 때 주로 사용하는 ORM 기법은 Order#cancel() 이나 Order#changeShippingInfo() 기능처럼 도메인 상태 변경 기능을 구현하는데는 적합하지만 주문 상세 조회 화면처럼 여러 애그리거트에서 데이터를 가져와 출력하는 기능을 구현하기에는 고려할 게 많아서 구현을 복잡하게 만드는 원인이 된다
구현 복잡도를 낮추는 간단한 방법은 상태 변경을 위한 모델과 조회를 위한 모델을 분리하는것이다

11-2. CQRS

CQRS: Command and Query Responsibility Segregation
CQRS 패턴을 적용하기 위해 사용해야 할 필수 기술이 따로 하는것은 아니다
시스템이 제공하는 기능은 크게 2가지로 나눌 수 있다
1.
상태 변경
개발자는 현재 정하고 있는 데이터를 변경하는 방식으로 기능을 구현한다
e.g.) 주문 생성, 배송지 정보 변경, 회원 암호 변경 등
2.
상태 정보 조회
조회 기능은 필요한 데이터를 읽어 UI 를 통해 보여주는 방식으로 구현한다
e.g.) 주문 상세 내역 보기, 게시글 목록 보기, 회원 정보 보기, 판매 통계 보기 등
도메인 모델관점에서 상태 변경 기능은 주로 한 애그리거트의 상태를 변경한다
상태 조회 기능에 필요한 데이터를 표시하려면 두 개 이상의 애그리거트가 필요할 때가 많다
CQRS 는 상태를 변경하는 명령 (Command) 와 모델과 상태를 제공하는 조회(Query) 를 위한 모델을 분리하는 패턴이다
CQRS 는 복잡한 도메인에 적합하다
도메인이 복잡할수록 Command 와 Query 기능이 다루는 데이터 범위에 차이가 난다
CQRS 를 사용하면 각 모델에 맞는 구현 기술을 선택할 수 있다
e.g.)
Command 는 객체기향에 기반해서 도메인 모델을 구현하기에 적당한 JPA 를 사용한다
Query 는 DB 테이블에서 SQL 로 데이터를 조회할 때 좋은 MyBatis 를 사용해서 구현한다
Command 모델과 Query 모델이 서로 다른 데이터 저장소를 사용할 수도 있다
Command 모델과 Query 모델이 서로 다른 데이터 저장소를 사용할 경우 데이터 동기화 시점에 따라 구현 방식이 달라질 수 있다
Command 모델에서 데이터가 바뀌자말자 변경 내역을 바로 Query 모델에 반영해야 한다면 동기 이벤트와 글로벌 트랜잭션을 사용해서 실시간으로 동기화
동기 이벤트와 글로벌 트랜잭션을 사용하면 전반적인 성능이 떨어지는 단점이 있다
서로 다른 저장소의 데이터를 특정 시간 안에만 동기화해도 된다면 비동기로 데이터를 전송한다
e.g.)
Command 는 트랜잭션을 지원하는 RDBMS 를 사용
Query 는 조회 성능이 좋은 메모리 기반 NoSQL 을 사용
명령 모델과 조회 모델 예시

11-2-1. 웹과 CQRS

일반적인 웹 서비스는 상태를 변경하는 요청보다 상태를 조회하는 요청이 많다
포털이나 대형 온라인 쇼핑몰과 같이 조회 기능 요청 비율이 월등히 높은 서비스를 만드는 개발팀은 조회 성능을 높이기 위해 다양한 기법을 사용한다
기본적으로 쿼리를 최적화해서 쿼리 실행 속도 자체를 높이고, 메모리에 조회 데이터를 캐싱해서 응답 속도를 높이기도 한다
조회 전용 저장소를 따로 사용하기도 한다
조회 성능을 높이기 위해 다양한 기법을 사용하는 것은 결과적으로 CQRS 를 적용하는 것과 같은 효과를 만든다
대규모 트래픽이 발생하는 웹 서비스는 알게 모르게 CQRS 를 적용하게 된다
다만, 명시적으로 Command 모델과 Query 모델을 구분하지 않을 뿐이다
조회 속도를 높이기 위해 별도 처리를 하고 있다면 명시적으로 명령 모델과 조회 모델을 구분하는게 좋다

11-2-2. CQRS 장단점

장점
Command 모델을 구현할 때 도메인 자체에 집중할 수 있다
조회 성능을 위한 코드가 Command 모델에 없으므로 도메인 로직을 구현하는데 집중할 수 있다
Command 모델에서 Query 관련 로직이 사라져 복잡도가 낮아진다
조회 성능을 향상시키는데 유리하다
조회 단위로 캐시를 적용할 수 있다
조회에 특화된 쿼리를 마음대로 사용할 수 있다
조회 전용 저장소를 사용하면 조회 처리량을 대폭 늘릴 수 있다
단점
구현해야 할 코드가 더 많아진다
도메인이 복잡하거나 대규모 트래픽이 발생하는 서비스라면 조회 전용 모델을 만드는 것이 향후 유지 보수에 유리하다
도메인이 단순하거나 트래픽이 많지 않은 서비스라면 조회 전용 모델을 따로 만들 때 얻을 이점이 있는지 따져봐야 한다
더 많은 구현 기술이 필요해진다
Command 모델과 Query 모델을 다른 구현 기술을 사용해서 구현하기도 하고, 경우에 따라 다른 저장소를 사용하기도 한다
데이터 동기화를 위한 메시징 시스템을 도입해야 할 수도 있다
도메인이 복잡하지 않은데 CQRS 를 도입하면 두 모델을 유지하는 비용만 높아지고 얻을 수 있는 이점이 없다