도메인 주도 개발 시작하기(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 를 도입하면 두 모델을 유지하는 비용만 높아지고 얻을 수 있는 이점이 없다