본문 바로가기
Architecture

CQRS 패턴이란? CQRS Pattern

by SOLYI 2023. 11. 9.

CQRS Pattern 

이 포스트는 출처(Milanjovanovic/CodeMaze)가 있는 글입니다. 공부를 위해 정리했습니다

 

 

 

CQRS 패턴( Command Query Responsibility Segregation )이란 명령 쿼리 책임 분리라고도 하며 최근 인기있는 디자인 패턴으로, 애플리케이션에서 읽기와 쓰기의 흐름을 논리적으로 분리하는걸 의미한다.

 

명령(Command)는 애플리케이션의 상태를 변경하는 데 사용된다 CRUD의 Create, Update, Delete를 다룬다.

쿼리(Query)는애플리케이션에서 정보를 검색하는 데 사용된다. CRUD의 Read를 다룬다.

 

CQRS 패턴은 다음과 같은 장점을 가진다.

 복잡도 관리(단일 책임) : 명령인지 쿼리인지에 대해서 단일 작업만 존재한다.

 향상된 퍼포먼스 : 명령과 쿼리에는 하나의 작업만 있으므로 추론하고 이해하기 쉽다.

 확장성 : 데이터 저장소 구성 방법 측면에서 유연하여 뛰어난 확장성을 옵션으로 제공한다.

 유연성 : 명령과 쿼리가 핸들러에서 완전히 분리되어 핸들러 측에서 가장 적합한 방식 구현을 위한 다양한 유연성을 제공한다.

 보안성 : 성능 향상을 위해 별도의 읽기/쓰기 데이터베이스를 사용하고 동기화를 위한 데이터베이스 간의 메시징 또는 복제를 사용할 수 있다.

 테스트 : 명령 또는 쿼리 핸들러는 설계상 매우 단순하고 단일 작업만 수행하므로 테스트하기 매우 쉽다.

 

다음과 같은 단점도 존재한다.

복잡성 : 고급 디자인 패턴이므로 완전히 이해하는데에 시간이 걸린다.

학습 곡선 : 간단해보이지만 대부분의 개발자는 코드 작성의 절차적 스타일에 익숙하며 CQRS는 그 스타일과 많이 다르다.

디버그 난해성 : 명령과 쿼리가 처리기에서 분리되어 있으므로 자연스러운 명령형 흐름이 없어서 디버깅하기 더 어렵다.

 

 

표준으로 여겨져 온 방식으로는 동일한 모델을 사용해서 데이터를 읽고 업데이트했었는데 이 방식은 간단하고 대부분의 CRUD 작업에 있어서 적합하다. 하지만 복잡한 애플리케이션에선 유지 관리가 어려워진다.

 

데이터를 추가하거나 수정하기 위해 복잡한 비지니스 로직이나 유효성 검사를 진행하기도 하고

데이터를 읽기 위해 다양하고 많은 쿼리를 실행해야 할 수도 있다.

 

또한 어떻게 데이터 모델을 만들지 고려해야 한다. SQL 데이터 모델링의 모범 사례는 정규화된 데이터베이스가 제공되는데 이는 일반적으로 괜찮지만, 데이터를 쓰는 데에 최적화되어있곤 한다.

 

명령과 쿼리를 각각의 모델로 사용하면 독립적으로 확장할 수 있다. 동일한 데이터베이스를 사용하더라도 논리적으로 분리할 수 있다. 명령 및 쿼리에 대한 하위 시스템을 각각의 서비스들로 분할할 수 있다. 또한 데이터 쓰기, 읽기에 최적화된 여러 데이터베이스를 사용할 수도 있다.

 

 

CQS 와는 어떻게 다를까?

CQS (Command Query Separtion) : 명령 쿼리 분리

 

기본 전제는 객체의 메서드를 명령과 쿼리로 분할한 것.

 

명령: 시스템의 상태를 변경하지만 값을 반환하지 않는다.

쿼리: 값을 반환하지만 시스템 상태를 변경하지 않는다. (사이드 이펙트 없음)

 

명령에서 값을 반환할 수 없다는 의미는 아니다. 

일반적인 예시로는 값을 반환한 뒤 시스템의 상태를 변경하는, 스택에서 값을 꺼내오는 경우가 있다.

 

CQRS는 CQS가 진화한 것으로, CQRS는 아키텍서 레벨에서 작동한다. 동시에  CQS는 메서드(혹은 클래스) 수준에서 동작한다.

 

 

CQRS의 다양한 선택

 

다음은 여러 데이터베이스를 사용하는 CQRS 시스템의 하이레벨 개요.

명령은 '쓰기' 데이터베이스를 업데이트하고, '읽기' 데이터베이스를 동기화해야 한다.

이를 통해 CQRS 시스템에 최종 일관성이 도입된다. 

 

최종 일관성은 애플리케이션의 복잡성을 크게 증가시키는데 동기화 프로세스가 실패하면 어떻게 될지 고려하고 내결함성 전략을 가져야 한다.

 

이러한 접근 방식에는 다양한 것들이 있다.

 

- 쓰기 위한 SQL 데이터베이스와 읽기 위한 NoSQL 데이터베이스

- 쓰기 위한 이벤트 소싱과 읽기 위한 NoSQL 데이터베이스

- 읽기 위한 Redis 사용 혹은 기타 분산 캐시 사용

 

읽고 쓰기 위한 데이터를 분리하면 요구사항에 가장 적합한 데이터베이스를 선택할 수 있다.

 

 

논리적 CQRS 아키텍처

CQRS 패턴을 시스템에 어떻게 적용할 수 있을까?

 

쓰기 측면에서는 일반적으로 EF Core와 rich 도메인 모델을 사용하여 비즈니스 로직을 캡슐화한다. 명령 흐름은 EF를 사용해서 메모리에 읽어오고, 도메인 로직을 실행하고, 변경사항을 데이터베이스에 저장한다.

 

읽기 측면에서는 가능한 간접적인 것을 추구하므로 Dapper사용하여 원시적인 SQL 쿼리를 사용하는 것은 좋은 선택이라고 할 수 있다. 데이터베이스에 view를 생성하거나 쿼리를 작성할 수 있다. 또는 EF Core를 사용해서 프로젝션으로 쿼리를 실행할 수도 있다.

 

 

 

 

정리

명령과 쿼리를 분리함으로써 장기적으로 성능과 확장성을 향상할 수 있다. 요구사항에 따라 명령과 쿼리를 각각 최적화할 수 있다.

 

명령어는 복잡한 비즈니스 논리와 검증을 캡슐화할 수 있다. EF Core나 rich 도메인 모델을 사용하는 것은 훌륭한 솔루션 중 하나다.

 

쿼리는 성능에 관한 것이므로 가장 퍼포먼스가 좋은 것을 사용하고자 Dapper를 사용한다. EF Core 프로젠션이나 Redis, 원시적인 SQL 쿼리를 사용할 수 있다.

 

 

 

/pages/architecturedesignpattern

반응형