도메인주도설계 5장
CQRS : 명령 모델과 조회 모델을 분리하는 패턴. 상테(데이터) 변경 기능 구현시에는 명령 모델, 데이터를 보여주는 기능을 구현할 때는 조회 모델 사용
# 검색을 위한 스펙
스팩 Specification : 검색 조건을 다양하게 조합해야 할 때 사용할 수 있는 것
agg는 애그리거트 루트, agg는 검색 결과로 리턴할 데이터 객체가 됨.
Spec 인터페이스 예시
Spec 인터페이스 구현 예시
만약 리포지터리가 메모리에 모든 애그리거트를 보관하고 있다면 다음과 같이 사용 가능하나, 실제로는 모든 데이터를 메모리에 저장을 못하기에 사실상 위와 같이 사용 불가능
실제 스펙은 사용하는 기술에 맞춰 구현하게 됨
# 스프링 데이터 JPA를 이용한 스펙 구현
JPA 크리테리아 API를 같이 이용
스펙은 and 혹은 or, where 등으로 조합이 가능함
# 개인적으로 가지는 의문점
1. 크리테리아 문법이 가독성이 떨어지는 것으로 보이는데, 차라리 QueryDSL 로 대체하면 안되나 ...?
# 정렬 지정하기
스프링 데이터 JPA에서 정렬 지정 방법
- 메서드 이름에 OrderBy를 사용
- Sort 인자를 전달
# 페이징 처리
find에 Pageable 파라미터를 사용하면 페이징을 자동으로 처리
페이징 처리와 관련된 정보가 필요 없다면 Page 리턴 타입이 아닌 List를 사용해서 불필요한 Count쿼리를 실행하지 않도록 함
처음부터 N 개의 데이터가 필요하다면 Pageable을 사용하지 않고 First 혹은 Top를 사용해도 됨
# 스펙 조합을 위한 스펙 빌더 클래스
스팩 빌더를 이용해서 다양한 스펙 조합 가능
# 동적 인스턴스 생성
# 하이버네이트 @Subselect 사용
@Subselect는 쿼리 결과를 @Entity로 매핑함
- @Subselect를 사용하면 쿼리 실행 결과를 매핑할 테이블 처럼 사용
- @Subselect로 조회한 @Entity 역시 수정 불가
- @Immutable을 사용하면 해당 엔티티의 매핑 필드/프로퍼티가 변경돼요 DB에 반영하지 않고 무시
- @Synchroize는 해당 엔티티와 관련된 테이블 목록을 명시, 엔티티를 로딩하기 전에 지정한 테이블과 관련된 변경이 발생하면 플러시를 먼저 함
여기서 궁금한게 CQRS와 이벤트소싱을 MSA에서 어떻게 사용하면 좋을지에 대해 판단을 해보았는데
인터넷에 찾아보니 딱 이 구조로 이벤트소싱과 CQRS패턴을 다음과 같이 사용하면 좋을 것같다.
사용자가 요청하는 요청이 쿼리인지 커맨드인지 판별하여 query일 경우 바로 db에서 조회하면된다 . ** 나중에 캐시처리하기가 너무 쉬워질거 같다.
쿼리부분-> 이부분은 트랜젹션에 의해 동시성문제가 발생할 수도 있고 다른마이크로서비스의 상태나 값을 변경해야할 수 도 있기떄문애
이벤트소싱을 사용하게되면 다양한문제를 해결할 수 있다.
1. 데이터베이스 메세지 대기열로 사용
2.트랜잭션 로그 마이닝 및 이벤트소싱을 포함하여 여러가지로 도움이 된다.
그럼여기서 이벤트소싱이란 ?
이벤트 소싱은 위와 같이 데이터를 변경하는 작업이 있을때 데이터가 있는 서비스에 요청하는 방식은 동일합니다.
그러나 한가지 다른 점이 있다면 데이터를 가진 서비스가 바로 데이터를 조작하지 않습니다.
위 그림을 보면 몇가지 요소가 있습니다 .
※ Event Producer
※ Event Consumer
※ Event (커맨드)
※ Event Store (큐)
※ Event Snapshot (빨간색 원)
대신에 위와 같이 이벤트를 발생시키고, Event Store 에 저장합니다.
Kafka 와 같은 메시지 브로커들이 Event Store가 될 수 있습니다. 그러면 이벤트 컨슈머가 해당 이벤트를 DB나 HDFS 에 들어온 순서대로 적재를 합니다.
그리고 필요한 데이터를 변경하거나 반영하게 됩니다.
이 구조를 사용하면 이전에 본 이슈들을 해결할 수 있습니다.
데이터 정합성:
MSA 구조에서 각 서비스마다 각자의 데이터를 가지고 있습니다.
이러한 데이터의 정합성을 맞추기 위한 방법이 여러가지가 있을 것입니다.
2phase 커밋이라는 방법도 있고, 분산 트랜잭션이라는 방법도 있습니다.
그런데 중요하게 고려해야할 사항이 있습니다. 트랜잭션은 Atomic 해야합니다. 그렇게 되면 모든 작업이 All or Nothing 으로 반영이 되거나 아예 안되거나 한 원자성을 가져야합니다.
그리고 이러한 작업이 진행되는 동안 트랜잭션 타임을 최소화 해야합니다.
사실 위와같이 처리하는 것은 매우 어렵습니다.
자칫 잘못하면 MSA 환경에서 시스템 전체 장애로 이어질 수 있습니다.
그래서 Event Sourcing 을 이용하면 바로 적용은 안되겠지만 eventual consistency를 얻을 수 있습니다.
오류재현
Event Sourcing 을 이용하면 오류를 재현할 수 있습니다.
Event Store 에 저장된 모든 이벤트들을 처음부터 수행해보면 문제를 찾을 수 있습니다.
그런데 매우 많은 이벤트들이 있다면 시간이 엄청 소모 됩니다. 그래서 우리는 중간중간에 Event Snapshot 을찍어 해당 시점동안의 정보 반영 결과를 스냅샷 해 놓습니다.
그럼 해당 스냅샷 시점 이후 이슈 발생시 스냅샷부터 리플레이를 해볼 수 있습니다
데이터 분석
특정 이벤트 데이터를 분석하거나, 어떠한 이벤트별 정보들을 보고자할때 우리는 저장된 이벤트 리스트를 이용하여 데이터를 분석도 쉽게 할 수 있습니다.
그리고 실제 데이터를 삭제하지 않고 순서대로 적재 하므로 전체 데이터 관점에서 분석 할 수 있습니다.
즉, 수정, 삭제의 개념보다 수정 이벤트, 삭제 이벤트를 저장하고 반영합니다.
스케일아웃
이벤트 소싱 패턴을 쓰면 스케일 아웃도 매우 쉽습니다.
이벤트를 시간순서대로 나열하면 되기 때문에 스케일 아웃이 되어 여러대의 서버가 이벤트를 발생하는 상황에서 매우 잘 적용이 됩니다.
CQRS (Command and Query Responsibility Segregation)
CQRS 는 커맨드 (Create, Update, Delete) 와 Query (Select) 를 책임을 분리해서 설계한다는 의미입니다.
이벤트 소싱에서 이벤트를 이벤트 스토리지에 저장하는 것을 위 그림에서 확인했습니다.
그때 우리는 이벤트 소싱이 필요한 커맨드에 대해서 창구를 하나 두고, 조회를 하는 창구를 별도로 둡니다.
CQRS 는 위외같이 쓰기/읽기 를 서로다른 인터페이스로 분리합니다.
이렇게 하면 다음과 같은 장점이 있습니다 .
※ 마이크로 서비스에서 데이터 처리에 이점이 있습니다. 특정 api 를 통해서만 데이터를 변경할 수 있고, 조회 api 를 이용해서 데이터를 읽을 수 있도록 분리합니다.
※ 읽기/쓰기 요청을 분리함으로 해서 성능을 개선할 수 있습니다.
※ 데이터 이상을 동시에 쓰는 방식보다 더 쉽게 찾을 수 있다.
※ 이벤트 소싱과 함께 사용되어 비동기 처리로 작업을 수행할 수 있다.