CQRS

Single Ko·2023년 8월 26일
0

공부하며 정리

목록 보기
15/17

Command Query Responsibility Segregation 의 앞글자를 따서 CQRS

간단하게 파악해보기..

간단하게 생각하자면? 데이터 저장소로부터의 읽기와 업데이트 작업을 분리하는 패턴

: 코드만 분리하는 것은 아님. 구현 방식, 규모에 따라 DB나 프로세스를 나누기도 한다.

Command Query (명령 쿼리)

명령

  • 시스템 데이터 변경
  • ex) 주문 취소, 배송 완료

쿼리

  • 시스템 데이터 조회
  • ex) 주문 목록 조회

Responsibility Segregation(책임 분리)

책임

  • 구성 요소의 역할
  • 구성 요소(모델) : 클래스, 함수 / 모듈,패키지 / 서버,DB

분리

  • 역할에 따라 구성 요소 나누기

정리를 해보자면?

명령(시스템 데이터 변경)을 책임지는 요소를 Segregation.
쿼리(시스템 데이터 조회)역할을 책임지는 구성 요소를 Segregation

Why CQRS?

  • 코드 중복, 개발해야 할 것이 더 많아 개발도 느려질거 같음

명령과 조회에 단일 모델을 사용한다면? - 필요한 필드가 점점 늘어나게됨

-> 코드의 역할/책임이 모호,
-> 의미/가독성 나빠짐
-> 유지 보수성이 떨어짐
-> 기능에 따라 필요한 필드가 달라진다

오히려 분리할때 보다 더 복잡하다

  • JPA를 사용할때 연관관계에 있는 테이블의 로딩 전략, Eager , Lazy 에 따른 복잡성.

  • 명령과 쿼리는 다루는 데이터가 다르다.
    - Command : 한 영역의 데이터
    - Queury : 여러 영역의 데이터

  • Command Query는 코드 변경 빈도, 사용자도 다름
    ex ) 사용자의 주문 기능, 백오피스의 주문 목록 조회 기능

  • 이렇게 변경 빈도가 다른 기능이 한 코드에 있다면?
    - 서로 다른 이유로 코드가 변경.
    - 책임의 크기가 맞지 않다(단일 책임 원칙 위배)

  • 기능마다 성능 요구가 다름
    - 트래픽 패턴, 성능 요구 다름
    (ex : 상품 조회(빨라야됨), 상품 등록, 주문, 판매수치 확인(조금 느려도됨..))

    -기능마다 서로 다른 성능 향상 방법 필요(단일 모델로는 다양한 성능 향상 기법 적용이 어려움)

명령과 쿼리 구분

  • 모호성이 사라짐. Command 모델과 Query 모델이 무엇을 표현하고 있는지 명확해짐.
  • 단일 모델에 비해 유지 보수성이 상승한다.
  • 성능 향상 기법을 다르게 적용(ex: Query - 캐시적용, Command - 비동기 사용 등..)

CQRS 구현

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

  • 가장 단순 : 코드 수준에서만 분리
  • 명령/쿼리 동일 데이터 보장
  • 트랜잭션 처리도 쉬움

2. 같은 프로세스, 같은 DB, 다른 테이블

  • 명령과 쿼리가 코드 수준에서 분리.
  • 데이터 수준에서도 분리(쿼리 전용 테이블 별도 사용)
  • 명령이 쿼리 전용 데이터 변경 유발.
  • 보통 명령이 상태를 변경할때, 쿼리 전용 테이블도 함께 변경

3. 같은 프로세스, 다른 DB

ex) 상품 목록을 레디스 같은 곳에 캐싱 -> 쿼리 모델은 레디스를 사용

  • 데이터를 변경시 쿼리쪽 DB까지 변경 전파를 해야함
  • 다양한 변경 전파 방법이 존재함.

4. 다른 프로세스, 다른 DB

보통 MSA 서비스의 형태에서 많이 접할 수 있는 구조.

다른 DB로의 전파

명령과 쿼리가 다른 DB를 사용하게된다면?

명령에서 일어난 변동사항을 쿼리쪽에 알맞게 전달해야 한다. 여러가지 전달 방법이 있다

3가지 방법 정도를 알아 보겠다.

  1. 명령이 직접 쿼리 DB를 수정하는 방식
    -> kafka와 같은 메시징 수단을 이용해 전달하는 변형도 있다.
  • 구현이 단순, 하지만 데이터 유실의 위험이 있다.
  • 순간적으로 DB나, 메시징 수단의 문제가 발생한다면 데이터가 유실된다.
  1. 변경 내용을 기록하고 별도의 전파기를 이용해 그 변경을 전달하는 방식
    -> 명령은 상태를 변경하고, 변경 내역을 별도의 테이블에 기록(한 트렌젝션으로 처리) : 변경 내역이 유실x, 전파기를 따로 구현해야 하는 까다로움이 있음.
  • 마찬가지로 [명령DB - 전파기 - 쿼리DB] 사이에 [명령DB - 전파기 - 메시징 - 쿼리DB] 을 따로 두는 변형이 있다.
  1. DB가 제공하는 CDC 사용하는 방법
    -> DB의 Binary Log를 읽어 변경점을 확인하고, 변경된 데이터를 쿼리DB 쪽에 전달하는 방식
  • 2번째 방식과 비슷하지만, 명령쪽 코드에서 변경내역을 따로 저장하지 않아도 됨.(명령 코드 단순)

  • 마찬가지로 메시징을 두는 변형방식이 존재

    변경전파 CDC에 대해 더 알고 싶다면? - (사용하는 DB) change data capture 로 검색해보면 좋다고 한다.

주의점

Command 와 Query가 다른 DB 사용시 주의사항

  • 데이터 유실 : 유실 허용 여부에 따라 DB 트랜잭션 범위 중요.

  • 허용 가능 지연 시간 : 명령의 변경 내역을 얼마나 빠르게 쿼리 쪽에 전달해야 하는가에 따라 구현의 선택이 달라질 수 있다.

  • 중복 전달 : 중복으로 데이터를 전달하더라도 쿼리쪽 데이터가 망가지지 않도록 처리해야함

다른 주의점도 많다. 이와 관련해서는 Messiging에 대해 알아보자

CQRS 에 대한 글

https://martinfowler.com/bliki/CQRS.html

https://gist.github.com/meigwilym/025f08208b5640ad26bc410c8a83b10f

profile
공부 정리 블로그

0개의 댓글