Azure 아키텍처, CQRS pattern

눕눕·2025년 3월 28일
0

Design pattern on Azure

목록 보기
8/9

CQRS pattern 이란?

CQRS (Command Query Responsibility Segregation)는 간단하게 표현하자면 읽기와 쓰기를 분리하여 처리하는 패턴이다.

예를 들면,

쇼핑몰 주문 시스템이라고 가정하였을 때,

  • Command

    1. 고객이 주문 생성 요청
    2. 주문 내역은 rdb에 저장
    3. 주문 생성 이벤트는 msg system에 전달
    4. consumer에서 msg를 기반으로 nosql에 주문 조회를 위한 document 생성
  • Query

    1. 고객이 주문 내역 조회 요청
    2. nosql 조회

그런데 왜 분리를 해야 할까?

장점

1. 확장성의 향상 & 성능 최적화
IT 플랫폼의 backend 쪽에서는 크게 2가지로 나누어 볼 수 있다. 읽기와 쓰기. 이 두 워크로드를 따로따로 스케일링이 가능한 장점이 있으며 코드 부분에서는 각각의 부분들을 특화된 구조로 가져갈 수도 있다.
2. 보안 및 권한 관리 용이
예를 들어, pod를 read와 write로 따로 분리하였다면 read pod는 read only db 계정을 가지고 작업할 수 있고 write pod는 write 권한이 들어간 db 계정으로 작업하면 된다. 조금 더 infra 적으로 들어간다면 istio 와 같은 service mash로 write db 또는 read only db로의 접근을 컨트롤 가능하며 이는 곧 보안이 더 강화된다는 말과 일맥상통한다.
3. 복잡한 도메인에 적합
DDD와 함께 사용하게 된다면 매우 세부적인 설계가 가능해진다.

단점

1. 복잡성 증가
하나의 도메인에서도 쓰기와 읽기가 분리가 되기에 복잡성이 올라가게 된다. 특히나 db의 경우 write가 가능한 db와 read만 하게 되는 db를 나누어 사용한다면 인프라 관리 측면에서도 난이도가 올라간다.
2. 데이터의 일관성
예를 들어, 동일 데이터라도 관리를 위해 rdb, nosql에 다 넣지만, read model에서는 nosql만 접근할 수 있다. 수정이 있을 경후, rdb에 수정하고 추후 빠른 read를 위해 nosql에 새로 저장한뒤 object id를 다른곳에 넣어야 하는데, 만약 event driven 형태로 이 과정이 실행된다면 데이터가 다를 수 있다.

Azure에서 어떻게 구현할 수 있을까?

Azure에서의 간단한 구성도

간단하게만 표현해본다면, 아래와 같이 표한할 수 있다.

내가 현재 서비스하고 있는 프로젝트도 위와 유사한 구조를 가지고 있다. 물론 위와 비교하였을 때, 중간에 스킵된 pod도 있고 저장되는 부분도 조금 다르긴 하지만 어느정도 선에서 CQRS를 따라가고 있다. 나중에 리펙토링 할 때 조금 더 디벨롭 할 부분이 많다.

위의 부분을 조금 글로써 설명해보자면,

Command

  1. apim을 통해 첫 번째 pod로 트래픽이 흘러간다.
  2. 첫 번째 pod에서 event hubs로 생성 되어야 할 정보가 event로서 발행된다.
  3. consumer에서 rdb에 저장한 후, 생성되었다고 event hubs로 또 event를 발행한다.
  4. 위 3번에서 발행된 event를 consuming 하는 pod에서 전달 받은 정보를 기반으로 cosmos db 에 생성된 정보를 기반으로 저장한다.

Query

  1. apim을 통해 첫 번째 pod로 트래픽이 흘러간다.
  2. read model pod로 트래픽이 전달되며 해당 pod는 cosmos db를 조회하여 결과값을 리턴하여 준다.

Tips!

  • Command와 Query는 철저히 분리
    읽는 코드에서 상태를 바꾸지 말고 상태 바꾸는 코드에서 조회하지 말자
  • Read 모델은 조회에 최적화된 구조로 설계하자
  • Read 모델이 읽어오는 대상 db에 너무 많은 필드를 넣지 말자
    너무 무거워지면 다시 RDB를 의존하게 되니 필요한 필드만 넣자
  • Write 모델은 정합성과 비즈니스 룰에 집중
    모든 핵심 검증, 상태 전이 등은 Command 처리 시점에 반드시 처리
    미리 검증하고 시간 지나서 command 처리하면 중복처리가 될 수 있다.
  • Read 모델은 항상 최신일 수 없다는걸 전제로 설계하자
  • 이벤트 중복 수신일 경우 두 번 처리해도 결과가 같게 만들자
    예를 들면 orderId 기준으로 upsert
  • Command -> Event -> Projection 흐름을 철저히 decouple 하자
  • 이력 관리도 잊지 말자
    audit 테이블, 히스토리 컬렉션, 이벤트 소싱 중 하나는 꼭 고려하자
  • CQRS는 만능 패턴이 아니다
    모든 아키텍처가 그렇듯 simple is the best
    복잡성만 증가시킬 가능성이 있을 수 있다
  • 모든 구성 요소를 모니터링/관찰 가능하게 만들자
    msg 큐 상태, dead-letter 큐, projection 지연 등 실시간 확인 가능해야 함
    로그, 트레이싱, DLQ 처리 전략 필수

가장 중요한 Tip,
CQRS는 정확한 분리와 비동기 흐름에 대한 이해가 핵심이다**

마치며

내가 진행하는 프로젝트는 CQRS를 조금 변형해서 사용하고 있다. 사실 SOT 자체가 외부에 있는 미들웨어 성격이 강하기에 어느정도 커스터마이징하여 사용하고 있는 중인데, 만약 올해 시간이 난다면 조금 더 다듬어 보고 싶다. 리펙토링 ㄱㅈㅇ

profile
n년차 눕눕

0개의 댓글