더보기
command (write) > 마스터만 보게 ,
쿼리 (조회) > 슬레이브만 보게
"프로젝트의 패키지 구조 및 컨벤션"
ex . PaymentService = paymentCommandService + paymentQueryService
DB > M / S Application > CQRS
부하를 줄여준다
1. CQRS패턴이란 ?
명령(Command)과 질의(Query)의 책임(Responsebilitiy)을 분리(Segregation)하여 시스템의 복잡성을 관리하는 아키텍쳐 패턴
쓰기를 위한 데이터 모델(Write Model)과 읽기를 위한 데이터 모델(Read Model)을 분리하는 패턴
복잡한 도메인 로직과 읽기/쓰기 요구사항이 뚜렷하게 다른 시스템에서 유용
1-1. 명령 모델(Command Model)
- 데이터를 변경하는 작업(쓰기 작업)을 처리
- 특정 명령을 받아서 비즈니스 로직과 유효성 검사를 수행하고, 데이터를 변경
- 일반적으로 서비스 로직과 관련이 있고, 데이터 생성, 수정, 삭제와 같은 작업을 담당
1-2. 쿼리 모델(Query Model)
- 데이터를 조회하는 작업(읽기 작업)을 처리
- 최적화된 읽기 작업을 위해 종종 데이터를 별도의 형태로 저장
- 데이터베이스나 다른 저장소에서 데이터를 읽고 결과를 반환하는 역할을 담당
2. 적용 방법
- 1단계 : 단일 Data Store 에 단일 어플리케이션 내에서 분리된 계층으로 나누는 방식
- 2단계 : 다중 DB , Broker를 통해서 이중화 된 데이터베이스를 동기화
- 3단계 : 이벤트 소싱(Event Sourcing) 패턴 (어플리케이션 내의 모든 처리 내용을 이벤트로 전환해서 이벤트 스트림을 별도의 DB에 저장하는 방식)
3. 장점과 단점
3-1. 장점
1. 확장성 (Scalability)
읽기 쓰기 분리로 각각 독립적으로 확장 가능
읽기 요청이 많은 시스템에서는 조회 부분만 별도로 최적화하거나 확장 가능
2. 성능 최적화 (Performance Optimization)
읽기 모델과 쓰기 모델을 각각 최적화 할 수 있음
예를 들어 조회 쿼리는 캐싱(Redis) , 비정규화된 데이터 구조 등을 활용하여 빠르게 처리 가능
3. 복잡성 관리(Separation of Concerns)
비즈니스 로직(쓰기)와 조회 로직을 분리하여 코드가 명확하고 유지보수가 쉬워짐
4. 유연한 데이터 모델링
읽기와 쓰기에 서로 다른 데이터 모델을 사용할 수 있어 각 요구사항에 맞는 데이터 구조 설계가 가능
5. 이벤트 소싱(Event Sourcing)과의 시너지 (이건 잘 이해가 안간다 )
데이터 변경 이력을 그대로 저장하고 재구성 가능
감사추적 (Audit Trail) 이나 복구 기능에 유리
6. 보안 강화
쓰기와 읽기 모델이 분리되므로 권한 제어를 더 정교하게 설정 가능
예: 읽기 전용 API와 쓰기 전용 API 분리
3-2. 단점
1. 복잡성 증가
읽기/쓰기 모델 간의 동기화 관리가 필요
2. 데이터 일관성 문제
읽기 모델과 쓰기 모델 간의 최종 일관성(Eventual Consistency) 문제가 발생 가능
실시간으로 데이터를 반영해야 하는 시스템에는 부적합할 수 있다
3. 개발 및 유지보수 비용 증가
초기 개발 비용과 유지보수 비용이 증가
배포, 테스트, 디버깅 과정이 더 복잡해질 수도 있다
4. 이벤트 관리의 어려움
이벤트 소싱과 함께 사용할 경우, 이벤트 버전 관리나 이벤트 스토어 관리가 까다로울 수 있음
5. 적용이 불필요할 수도 있음
단순한 CRUD 기반 애플리케이션에서는 오히려 과한 설계가 될 수 있으며, 단순한 요구사항에 CQRS를 적용하면 오히려 개발 속도 저하로 이어질 수 있음
* CQRS가 적합한 경우
• 복잡한 도메인 로직을 다루는 시스템 (ex. 금융 거래 시스템, 전자상거래 플랫폼)
• 읽기/쓰기 비율이 불균형한 시스템 (ex. 읽기 요청이 월등히 많은 경우)
• 확장성과 성능이 중요한 시스템
• 이벤트 소싱을 필요로 하는 시스템
* CQRS가 불필요한 경우
• 단순한 CRUD 애플리케이션
• 작은 팀이나 짧은 개발 주기를 가진 프로젝트
• 실시간 강력한 일관성이 필요한 시스템
예시
프로젝트 구조
src/main/java/com/example/cqrs/
│
├── command/
│ ├── controller/
│ │ └── ProductCommandController.java
│ ├── service/
│ │ └── ProductCommandService.java
│ └── model/
│ └── Product.java
│
├── query/
│ ├── controller/
│ │ └── ProductQueryController.java
│ ├── service/
│ │ └── ProductQueryService.java
│ └── dto/
│ └── ProductDTO.java
└── repository/
└── ProductRepository.java
• command/: 쓰기(등록, 수정, 삭제) 책임
• query/: 읽기(조회) 책임
• repository/: 데이터 저장소 (공통)
참고
https://terrys-tech-log.tisory.com/48
https://f-lab.kr/insight/understanding-cqrs