본문 바로가기

[Project] Petogether

CQRS를 이용한 관심사 분리

CQRS(command query responsibility separation) 패턴이란?

CQRS 패턴은 명령(Command)과 조회(Query)를 분리하여 성능과 확장성 및 보안성을 높일 수 있도록 해주는 아키텍처 패턴입니다. 요구 사항이 복잡해질수록 도메인 모델 역시 복잡해집니다. 데이터를 조회한 쪽에서는 현재의 복잡한 모델 구조의 데이터가 필요하지 않은 경우가 대부분이므로 조회 시의 모델과 데이터를 업데이트할 때의 모델을 다르게 가져가도록 하는 방식입니다.

 

조회는 CRUD에서 read 동작을 담당하는 요청을 뜻합니다. 리소스를 조회한 경과만을 반환하므로 시스템의 상태를 변경하지 않으며 부작용이 없습니다. 명령(command)은 create, update, delete 요청과 같이 시스템의 상태를 변경합니다

 

CQRS로직

 CQRS로직을 구조도로 표현하였습니다.

 

조회(Query) :

1.데이터베이스를 읽습니다.

2.조회 서비스는 조회 모델로부터 얻은 데이터를 이용하여 표현 계층을 갱신합니다. 즉, UI화면을 데이터에 맞게 적절히 표현합니다.

 

명령(Command):

1. 사용자는 UI를 통해 변경을 일으킵니다.

2. 애플리케이션 라우터는 정보를 명령 모델로 변환합니다.

3. 명령 모델은 유효성 검사 및 관련 로직을 수행합니다.

4. 명려 모델은 데이터베이스를 갱신합니다.

 

CQRS 패턴을 사용하면 좋은 경우

CQRS를 사용하면 몇 가지 복잡한 도메인을 다루기 더 쉬운 경우

: CQRS를 사용하면 복잡성이 추가되기 때문에 생산성이 감소하므로, 모델을 공유하는 것이 도메인을 다루기 더 쉬운지 면밀하게 판단해야 합니다. 특히 시스템 전체가 아닌 도메인 주도 설계에서 말하는 bounded context 내에서만 사용해야 합니다

 

고성능 처리가 필요한 애플리케이션을 다루는 경우

: CQRS를 사용하면 읽기 및 쓰기 작업에서 로드를 분리하여 각각을 독립적으로 확장할 수 있습니다. 클라우드 데이터베이스를 사용한다면 손쉽게 읽기, 쓰기 DB를 분리할 수 있습니다. 또 성능을 위해 쓰기는 RDB로, 읽기는 도큐먼트 DB를 사용하는 경우도 많습니다. 애플리케이션에서 읽기와 쓰기 사이에 큰 성능 차이가 있는 경우 CQRS를 쓰면 편리합니다. 또한 양쪽에 서로 다른 최적화 전략을 적용할 수 있습니다.

CQRS 패턴으로 프로젝트 구조 설정

지금까지 만든 조그만 유저 서비스는 회원가입, 로그인 간단한 비즈니스 로직만을 가지고 있습니다.

물론 여기에 핵심 기능을 더 붙여가면서 서비스를 확장하겠지만 여전히 당분간 그 크기는 작을 것입니다.

이렇게 작은 서비스에는 로직이 단순하고 변경이 생겨도 크게 영향을 끼치지 않습니다.

 

하지만 서비스가 커질수록 변경 영향도는 점차 커지게 되고, 컨트롤러와 서비스, 영속화 및 도메인 레이어에서 주고받는 데이터가 복잡해질 뿐 아니라 콘텍스트가 상이한 곳에서 모델을 그대로 전달하고 사용하는 경우가 발생합니다.

 

기존 아키텍처 패턴

 

 

Users에 기존 아키텍처 패턴입니다.

Users로 들어온 CRUD작업을 모두 service 에서 로직을 수행합니다.

기존까지 했던 작업들은 아직 규모와 단위가 작아서 문제가 없었지만 점차 규모가 커지고 복잡해지면 해당 구조보다 더 단일화된 패턴이 필요합니다.

 

Users에 CQRS적용

 

Command, Event, Query로 패턴을 나누었습니다.

 

Command는 CRUD에서 create, update, delete로직을 수행합니다.

Query는 Read를 수행합니다.

 

Event는 이벤트 핸들러 로직을 수행합니다.

Event의 예를 들어보겠습니다.


회원 가입을 처리하는 과정에 이메일을 전송하는 로직.

인증 이메일을 발송하는 것은 도메인 규칙상 반드시 필요한 과정이지만 이메일 발송은 회원 가입과는 별개로 다룰 수 있는 것입니다.

 

만약 이메일 인증을 다른 인증 수단으로 변경한다면 회원 가입 로직을 다시 수정해야 하므로 회원가입과 이메일 인증 로직의 강한 결합이 존재하면 안좋습니다.

 

이럴 경우 회원 가입 이벤트를 발송하고 이벤트를 구독하는 다른 모듈에서 이벤트를 처리하도록 하는 것이 좋습니다.

따라서 Event모듈로 비즈니스 모듈을 분리하여 구현합니다.