이러한 쿼리는 좋은 아키텍처에 적합합니까?
나는 한동안 문제에 대해 생각 해왔고,이 문제에 접근하는 방법을 제대로 못 잡았다 고 생각하지 않습니다.
가능한 한 DDD로 작업하려고 노력하고 있지만 일부 프레젠테이션 시나리오는 성능 측면에서 실제로 적합하지 않습니다.
렌트카 시스템이 있고 여기에 모든 차량과 차량이 렌트 된 횟수를 표시해야하는 뷰가 있다고 가정 해 보겠습니다.
DDD 세계에서는 내 렌트카를위한 저장소와 "렌트 아웃"을위한 저장소가있을 것 같습니다. 나는 또한 그들 사이의 상호 작용을 처리하고 계약을 작성하는 서비스가있을 수 있다고 생각합니다.
하지만 렌트카와 렌트카 수를 보여줄 때는 어디에 두어야할지 잘 모르겠습니다. 요구 사항은이 뷰에 매우 구체적이기 때문에 가장 성능이 좋은 버전은 저장소에서 반환하는 일종의 "RentalCarsOverview"개체를 갖는 것입니다. 그러나 이것은 뷰에 특화된 쿼리로 저장소를 "오염"시킬 것입니다.
그래서 누군가 같은 문제에 직면했는지 그리고 어떻게 해결되었는지 궁금합니다. 나는 이러한 종류의 뷰에 대한 쿼리와 업데이트를위한 내 도메인 모델을 사용하여이를 분리하기 위해 CQRS를 살펴 보았습니다. 이것이 좋은 접근 방식입니까?
또한 이러한 쿼리 처리기에 원시 SQL 만있는 것이 "괜찮습니까"아니면 기본 저장소로 이들을 추상화합니까?
답변
요즘 일반적인 대답은 쿼리 코드 경로가 "도메인 모델"을 우회하고 정보 사본으로 직접 작업한다는 것입니다.
여기서 주장은 쿼리가 도메인 모델에 저장된 정보를 변경하지 않기 때문에 정보를 도메인 개체에 다시 가져가는 방식으로 이익을 얻지 못한다는 것입니다.
예를 들어, 모든 정보가 동일한 관계형 데이터베이스에 저장되어있는 경우 데이터베이스를 쿼리하고 결과 집합의 정보를 올바른 모양으로 변환 한 다음 클라이언트로 다시 보낼 가능성이 높습니다.
쿼리에 응답하는 데 필요한 정보가 여러 시스템에 분산되어 있으면 보고서를 생성하기위한 더 많은 맞춤형 작업이 있습니다. 보고서를 생성하는 데 필요한 시간이 지연 목표를 초과하면 보고서 의 값 비싼 부분을 캐시 할 수있는 설계를 조사하기 시작 합니다. 어떤 경우에는 전체 보고서가 캐시됨을 의미합니다.
CQRS와 DDD는 완벽하게 일치합니다.
명령은 저장소 및 DDD 모델을 통해 이동합니다. 쿼리는 원시 SQL 또는 경량 ORM 일 수 있으며 각 뷰에 대해 특별히 별도의 모델이 있습니다.
이러한 방식으로 시스템을 설계하면 읽기에 대한 최상의 성능으로 복잡한 비즈니스 규칙을 처리 할 수있는 확실한 방법이 제공됩니다.
스마트 쿼리를 갖는 것이 DDD 코드 냄새임을 지적 해 봅시다. 대부분의 경우해야 할 일은
- ID로 집계 검색 (RESTful : GET / users / xyz)
- 주어진 루트 (/ users? page = 0 & limit = 10)로 모든 집계 검색
- 몇 가지 간단한 필터로 집계의 하위 집합을 검색합니다 (RESTful : GET / users? username_startsWith = johnd & page = 0 & limit = 10).
복잡한 애플리케이션은 복잡한 작업을 명확하고 명확하게 수행해야하므로 집계에서 프로젝션을 작성 해야 할 수도 있습니다 . 분명히 집계는 프로젝션이 무엇인지에 대한 단서를 가져서는 안되지만, 도메인을 지속적으로 변경 될뿐만 아니라 준수하도록 설계하기 때문에이를 구축하는 데 필요한 모든 데이터를 찾을 수 있습니다.
이것은 CQRS 개념을 도입하지 않고도 합리적인 접근 방식입니다. 대부분의 애플리케이션은 CRUD 애플리케이션 + 일부 도메인 유효성 검사 규칙입니다. 요구 사항 / 스토리 / 코드에서 " 개요 " " 보고서 " " 통계 " " 요약 " " 내역 " 과 같은 단어를 몇 번만 만나면 특정 쿼리를 통해 메모리에 집계를로드하고 일부 집계를 호출하여 관리 할 수 있음을 알고 있습니다. 방법 (개수, 합계, 평균) e 일부 투영 DTO에 대한 매핑 수행
앞서 언급 한 사례가 프로젝트에서 두드러진 경우 집계를로드하여 일부 집계 정보 만 검색하는 것은 시스템에 큰 부담이되므로 예, CQRS가 도움이되는 두 가지 사용 사례 중 하나를 충족하는 것입니다.
주목할만한 상태 변이가 커밋 될 때마다 도메인 이벤트를 트리거하는 기능을 집계 (즉, 쓰기 측 집계)에 제공합니다.
프로젝션 을 업데이트하고 저장하기 위해 프로젝션 특정 리포지토리 및 이벤트 핸들러 (귀하의 경우 onEvent (CarRentedOutEvt evt) 와 같은)가있는 다른 모듈을 빌드합니다 .
매우 멍청하게 동작하는 GET / rents / overview 관리 API를 (다시 한 번 안타깝게 말하면) 갖게 될 것입니다. 모든 복잡성은 업데이트시 오프로드됩니다.
둘 이상의 읽기 모델을 가질 수 있으며,이 경우 "Rented Out"이벤트를 수신 할 때 자체 업데이트에 필요한 이벤트 만 구독하는 비정규 화 된 "Reporting"읽기 모델이 있습니다.