Programming

[CQRS 아는 척하기] CQRS의 기본 개념

nineDeveloper 2021. 1. 5.
728x90

https://www.youtube.com/watch?v=xf0kXMTFJm8&list=PLwouWTPuIjUgr29uSrSkVo8PRmem6HRDE&index=5

 

CQRS(Command Query Responsibility Segregation)

Command Query (명령과 쿼리)

명령

  • 시스템 데이터 변경
  • 기능 예
    • 주문취소, 배송완료

쿼리

  • 시스템 데이터 조회
  • 기능 예
    • 주문 목록

Responsibility Segregation (책임 분리)

책임

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

분리

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

CQRS 란?

명령(시스템 데이터 변경) 역할을 수행하는 구성 요소
쿼리(시스템 데이터 조회) 역할을 수행하는 구성 요소나누는 것CQRS

쉽게 말해서 시스템 데이터를 변경하는 코드시스템 데이터를 조회하는 코드를 따로 만드는 것이라고 볼 수 있음
구현방식이나 시스템 규모에 따라서 DB를 나누기도하고 프로세스를 나누기도 함


간단한 예

  • MemberCommandApi: 명령을 위한 구성요소
    • 명령 상태를 변경하는 기능
  • MemberQueryApi: Query를 위한 구성요소
    • 상태를 조회하는 기능


뭐가 좋은지?

  • 코드가 중복되는 느낌?
  • 개발이 느려지는 느낌?
  • 그런데 왜?


명령과 조회에 단일 모델을 사용하면?

  • 당장 쉽게 하고 싶은 마음

  • 기능에 따라서 사용하는 필드가 달라짐

    • 예를 들어 이름변경 기능에서는 Member 테이블에 해당하는 데이터만 읽어와서 Member 객체를 만들고
      주문목록 조회 기능에서는 세개의 테이블을 이용해서 Member 객체를 만들게 됨
  • 이도 저도 아닌 잡탕

    • 코드 역할/책임 모호
    • 의미/가독성 등 나빠짐
    • 시스템이 복잡해질 수록 유지보수성이 떨어짐

  • 더 복잡해지거나 (JPA 예)

어떤 로딩(eager, lazy)을 사용할지는 기능에 따라 다름

  • 주문을 취소하는 기능
    • Order, OrderLine 만 있으면 되므로 Order를 로딩할때 Usereager로 로딩할 필요가 없음
  • 주문의 목록을 조회하는 기능
    • 사용자 정보를 함께 보여줘야 하므로 UserJoin 해서 eager로 로딩

단일 모델을 유지하려고 노력하다 보면 다른 부분에서 복잡한 일이 생기게 됨


명령과 쿼리는 다루는 데이터가 다름

복잡해지는 시스템에서 명령과 쿼리의 단일 모델을 구사하면 유지보수성이 떨어짐

  • 가장 큰 이유는 명령과 쿼리가 다루는 데이터가 다름
    • 애초에 아주 단순한 시스템이 아니면 명령과 쿼리를 같은 모델에 담을 수 없음
  • 명령 -> 한 영역의 데이터
    • 주문취소와 같은 기능은 Order, OrderLine만 다룸
  • 쿼리 -> 여러 영역의 데이터
    • 회원목록 조회, 주문목록 조회와 같은 기능은 Order, User, Item 영역의 데이터를 사용하게 됨


명령과 쿼리는 코드 변경 빈도, 사용자 다름

    • 백오피스의 주문 목록 조회 기능
    • 사용자의 주문 기능
    • 사용자도 백오피스 운영자와 상품을 구매하는 고객으로 다르다
  • 변경 빈도가 다른 기능이 한 코드에 있으면
    • 서로 다른 이유로 다른시점에 코드가 바뀌고
    • 이는 곧 해당 코드가 가지고 있는 책임의 크기가 적당하지 않다는 것 (단일 책임 원칙을 따르지 못하는 코드)

기능마다 성능 요구가 다름

  • 기능마다 트래픽 패턴, 성능 요구가 기능마다 다름
    • 사용자의 상품 목록 조회, 상품 상세 조회
      • 트래픽이 상대적으로 많은 시간대가 있고 빨리 보여줘야 함
      • 상품 목록 조회가 지연되면 고객이 이탈함
    • 사용자의 댓글 등록
    • 사용자의 주문
    • 백오피스의 판매 수치
      • 조금 늦게 나와도 상관 없음
  • 기능마다 서로 다른 성능 향상 방법 필요
    • 단일 모델로는 다양한 성능 향상 기법 적용이 어려울 수 있음

그래서 명령과 쿼리를 구분

명령과 쿼리를 위한 모델을 분리하면 앞에서 언급했던 문제들이 줄어들게 됨

  • 모델의 모호함이 없어짐
    • 명령영역의 모델과 쿼리영역의 모델이 무엇을 표현하고 있는지가 명확해짐
    • 전반적인 코드 가독성과 유지보수성이 좋아질 가능성이 높아진다
    • 쿼리 쪽은 캐쉬를 적용하고 명령 쪽은 비동기를 적용하는 식으로 기능에 따라서 성능향상 기법을 다르게 적용하는 것도 용이해짐

728x90

댓글

💲 추천 글