본문 바로가기

Book

명령-질의 책임 분리(CQRS)

논란의 여지가 없는 부분, 읽기(질의)와 쓰기(명령) 은 다르다. 다르게 취급해야 함. 근데 왜 이렇게 해야 하나?

이전에 짧게 봤던 개념인데 여기서 구체적으로 구현하는 방식 중 하나를 소개한다. 하지만 책에서 소개하는 방법은 같은 프로세스, 같은 DB 내에서 구현하는 방식을 소개함. 거기서 테이블 정도만 달라지는 방식.

12.1 쓰기 위해 존재하는 도메인 모델

지금까지 했던, 작업단위, 애그리게이트, 이벤트 패턴등은 상태를 변경할 때 규칙 적용을 강화하기 위해 존재한다. 즉 데이터를 유연하게 쓰기 위해서 도구를 만들었던 것임. 그런데 읽기는 좀 다름

12.2 가구를 구매하지 않는 사용자

결론적으로 무엇을 해도 현실은 소프트웨어 시스템과 일관성이 없다고 함. 그래서 비즈니스 프로세스가 모든 경우를 처리할 수 있어야 함. 전에도 말했듯이. 지금까지 만든 복잡한 도메인 패턴은 데이터를 읽는 데는 아무 역할이 없음.

12.3

CQS(Command-query separation) 예시로 POST/redirection/Get이 있음. 함수는 상태를 변경하거나 질문에 답하는 일 중에 한 가지만 해야 한다. 두 가지 일을 모두 다 해서는 안 된다. 그래야지 소프트웨어를 더 쉽게 추론할 수 있다.

12.4 ~ 12.10

여기서는 구체적으로 CQRS 구현 예시가 나옴. 3가지 대안을 제시함

  1. 기존 저장소 사용
  2. ORM과 커스텀 질의를 사용
  3. native 한 SQL을 사용
  4. 이벤트 사용해서 별도 읽기 저장소(테이블 추가)
    모든 방식이 트레이드오프가 있긴 함.
    먼저 기존 저장소 사용하면 여러 부분을 수정해야 한다. 도우미 함수들을 추가해야 함

12.7

수정하는 과정 보면 도메인 모델들이 기존에 만들어질 때 쓰기 연산을 위한 것임을 알 수 있음. 읽기를 위한 요구 사항은 개념적으로 도메인 모델에 대한 요구 사항과 상당히 다르다고 함. 도메인 모델은 데이터 모델이 아니다.
워크플로, 상태 변경을 둘러싼 규칙, 메시지 교환 등 비즈니스의 작업을 잡아내려고 시도 중임. 이는 시스템이 외부 이벤트와 입력에 대해 어떻게 반응하는지에 대해 주로 관련되어 있음. 이런 요소 중 대부분은 읽기 전용 연산과는 전혀 관계가 없다.

12.8

다음으로는 ORM를 사용해서 구현하는 방식이다. 내가 보기에는 괜찮았는데, 여기서는 어차피 SQL 쓰는 거나 ORM 쓰는 거나 더 쉽게 이해할 수 있을 거냐고 하는데, 이건 익숙함 차이인 거 같다 그거 말고는 이 방식도 나쁘진 않은 거 같음.

12.9

여기서는 SELECT N+1 문제를 얘기하는데 ORM에서 주로 생길 수 있다고 한다. 근데 ORM 사용할 때 발생 안 하게 하는 방식들이 있어서 이거 자체는 크게 문제는 안되는 거 같다. 아무튼 여기서는 정규화된 테이블은 데이터 정합성 면에서는 좋은데 성능이 느려질 수 있어서 정규화되지 않은 뷰를 추가하거나, 읽기 전용 복사본을 만들거나, 캐시 계층을 추가하면 좋다고 한다.
그래서 다음 절은 이런 방식으로 만들어서 구현한다.

12.10

위에서 말한 거처럼 정규화하지 않은 데이터 테이블을 별도로 만들어서 관리함. 실제로 이런 경우는 드물지 않다고 함. 특히 인덱스를 사용해 처리할 수 있는 일의 한계를 맛보고 나면 이런 복사본을 만들게 된다고 한다. 아무리 잘 튜닝한 인덱스가 있어도 조인을 위해서 CPU를 많이 사용한다.

```
#기존 sql
SELECT ol.sku, b.reference 
FROM allocations AS a 
JOIN batches AS b ON a.batch_id = b.id 
JOIN order_lines AS ol ON a.orderline_id = ol.id 
WHERE ol.orderid =:orderid
#테이블 추가후
SELECT sku, batchref FROM allocations_view WHERE orderid = :orderid
```

추가한 테이블

```
allocations_view = Table(
"allocations_view",
metadata,
Column("orderid", String(255)),
Column("sku", String(255)),
Column("batchref", String(255)),
)
```

이런 접근 방식의 장점은 질의의 실행속도가 빨라진다는 점 외에도 규모를 확장할 수 있다는 장점이 있다.
읽기용 복사본이 일관성이 없을 수도 있어서 사용할 수 있는 복사본 수에는 한계가 있다. 복잡한 데이터 저장소가 있는 시스템의 규모를 확장하는 데 어려움이 있다면 더 간단한 읽기 모델을 만들 수 없는지 살펴봐야 함.

 

추가로 좀 더 찾아보면 처음 언급한 거처럼, 책에서는 같은 프로세스 같은 DB를 가정한 거 같음, 딱히 얘기가 안 나와서. 근데 CQRS 방식을 좀 더 찾아보면 다른 DB를 사용할 때 신경 써야 할게 좀 더 있음. DB를 동기화하는 과정이 필요하니 여러 방식들이 있음 보통 마이크로 서비스에서 볼 수 있음 이건 따로 정리해야겠음. 

 

 

참고 및 출처: <파이썬으로 살펴보는 아키텍처 패턴: TDD, DDD, EDM 적용하기)> (해리 퍼시벌, 밥 그레고리 지음, 오현석 옮김, 한빛미디어 , 2021)

'Book' 카테고리의 다른 글

계층형 설계 1,2  (2) 2023.12.04
의존성 주입(그리고 부트스트래핑)  (0) 2023.11.16
커맨드와 커맨드 핸들러  (0) 2023.10.29
메시지 버스로 통합하기  (2) 2023.10.22
오브젝트. Chapter 01 객체,설계  (0) 2023.10.18