--- title: company-tech-blog / Explicit Architecture — DDD, Hexagonal, Onion, Clean, CQRS 통합 (Herberto Graça) source_type: company-tech-blog url: https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ archive_url: related_branches: [feature-application-query-bypass-contract] related_projects: [ca-skeleton] tags: [architecture, hexagonal, cqrs, query-handler, read-model, application-service, ddd, clean-architecture, ca-skeleton] created: 2026-06-04 last_reviewed: 2026-06-04 --- # Explicit Architecture — DDD, Hexagonal, Onion, Clean, CQRS 통합 (Herberto Graça) > Layer: `raw/company-tech-blogs/` — Herberto Graça 의 "DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together" (2017-11-16) 발췌. hexagonal 아키텍처에서 CQRS query handler 가 Application Service (Use Case) 를 어떻게 다루는지의 대표적 설명. > > **출처 신뢰도**: `engineering-blog` 등급 — 저자의 개인 기술 블로그. 공식 표준 아님. 그러나 DDD/hexagonal/CQRS 통합 설명에서 커뮤니티에서 자주 인용되는 article. ## Parent / 활용 branch (필수) | Branch | 이 자료가 정당화하는 결정 | |---|---| | [[raw/branch-notes/feature-application-query-bypass-contract]] | D2 (use-case layer ceremony 의 bypass — CQRS query handler 가 Application Service 를 거치지 않고 직접 optimized query 를 실행하는 패턴) 의 architectural reference. query side 가 "optimized query that will simply return some raw data" 로 작동하는 설계 근거 | ## 출처 / Source - 원본 URL: https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ - 아카이브 URL: - 저자 / 조직: Herberto Graça — 개인 기술 블로그 (hgraca.com) - 발행일: 2017-11-16 - 마지막 확인일: 2026-06-04 ## 왜 저장했는지 / Why archived ca-tmpl 의 alternative 3 (CQRS query handler pattern) 의 architectural reference. Graça 는 hexagonal + CQRS 통합 설명에서 query handler 가 Application Service 와 다른 역할을 한다는 것을 명시적으로 설명. 특히 "The Query object will contain an optimized query that will simply return some raw data" 는 query side 가 domain aggregate 로딩 없이 직접 DTO 반환이 가능함을 시사. ## 핵심 인용 / Key quotes (verbatim) > [§CQRS query side — query object] "The Query object will contain an optimized query that will simply return some raw data to be shown to the user." > [§Application Services — role definition] "Application Services (also known as workflow services, use cases, or interactors) are used to orchestrate the steps required to fulfill the commands imposed by the client." > [§Application Services — typical steps] "1. use a repository to find one or several entities; 2. tell those entities to do some domain logic; 3. and use the repository to persist the entities again." > [§Command/Query Bus without separate bus] "Controllers can depend on Query objects [directly], distinct from Application Services." > [§DTO for view] "That data will be returned in a DTO which will be injected into a ViewModel." ## Claims Extracted / 추출된 주장 | Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove | |---|---|---|---|---|---| | HGRACA-CQRS-C1 | CQRS query side 에서 Query object 는 **optimized query** 를 담고 user 에게 보여줄 **raw data** 를 반환하도록 설계됨 — 도메인 aggregate 조작 없이 단순 데이터 반환 | [§CQRS query side] "The Query object will contain an optimized query that will simply return some raw data to be shown to the user." | `engineering-blog` | CQRS query side 설계 — query handler 가 Application Service 를 거치지 않는 패턴 | "모든 읽기가 Use Case Application Service 를 거칠 필요 없다" 는 공식 표준이 아님 — engineering-blog 등급의 저자 설계 의견 | | HGRACA-CQRS-C2 | Application Service (Use Case, Interactor) 의 역할은 **command 를 오케스트레이션** — entity 를 repository 로 find, domain logic 실행, repository 로 persist 하는 3단계 | [§Application Services] "Application Services...are used to orchestrate the steps required to fulfill the commands imposed by the client." + "1. use a repository to find one or several entities; 2. tell those entities to do some domain logic; 3. and use the repository to persist the entities again." | `engineering-blog` | command side (write path) 의 Application Service 역할 정의 | **query side** 에도 Application Service 가 필요하다는 주장의 근거는 아님 — 이 3단계는 command 를 대상으로 명시 | | HGRACA-CQRS-C3 | query side 에서 반환되는 데이터는 **DTO** 형태로 ViewModel 에 주입됨 | [§DTO for view] "That data will be returned in a DTO which will be injected into a ViewModel." | `engineering-blog` | CQRS query side 반환 타입 — application layer 가 JPA entity 를 직접 반환하지 않음 | DTO 가 반드시 별도 record/class 여야 한다는 강제는 아님 — interface projection 도 DTO 패턴의 변형으로 간주 가능 | | HGRACA-CQRS-C4 | Query Bus 없는 구조에서 **Controller 가 Query object 에 직접 의존** 하는 패턴이 제시됨 — Application Service 를 거치지 않는 thin read path 의 구조적 근거 | [§Without Command/Query Bus] "Controllers can depend on Query objects [directly], distinct from Application Services." | `engineering-blog` | Command/Query Bus 를 별도 도입하지 않는 단순 CQRS 구현 | Controller 가 Query object 에 직접 의존해도 hexagonal 의 **transport 타입이 application layer 에 leak 해선 안 된다** 는 제약은 본 인용이 직접 다루지 않음 | ## Usage Boundaries / 적용 경계 - 이 자료가 직접 증명하는 것: - `HGRACA-CQRS-C1`: query side 가 "optimized query + raw data return" 으로 작동할 수 있다는 설계 제안 - `HGRACA-CQRS-C2`: Application Service 의 3단계 오케스트레이션은 **command path** 에 명시적으로 귀속 - `HGRACA-CQRS-C3`: query side 반환 타입은 DTO (domain entity 가 아님) - `HGRACA-CQRS-C4`: Controller → Query object 직접 의존 패턴 (bus 없는 CQRS) - 이 자료가 증명하지 않는 것: - hexagonal 아키텍처에서 "thin read path" 에도 transport type (HTTP, gRPC) 이 application layer 에 leak 하지 않아야 한다는 설계 제약 — Graça 의 diagram 은 이 경계를 명시하지만 본 발췌 인용에는 포함되지 않음 - Query object 또는 query handler 를 ArchUnit 으로 정적 강제하는 방법 - Spring Boot 환경에서 query handler 를 어느 Gradle module 에 배치하는지 - transaction 없는 thin read path 의 Hibernate session 동작 - 내 프로젝트에 적용하려면 추가 확인이 필요한 것: - ca-tmpl 의 thin read path 에서 `@Controller` (org.springframework.web) 타입이 application port 에 노출되지 않도록 query service / read port 를 어느 layer 에 배치할지 결정 (D1 의 핵심) - ca-tmpl 의 ArchUnit rule 이 Controller → QueryService 직접 의존을 허용할지, 또는 QueryUseCase (인터페이스) 를 항상 중간에 두도록 강제할지 ## 메모 / Notes - Graça 의 글은 2017년 작성이지만 hexagonal + CQRS 통합에서 가장 자주 인용되는 레퍼런스 중 하나. 한국어 번역본도 존재. - HGRACA-CQRS-C1 의 핵심 의미: query 는 domain 오케스트레이션 없이 read-optimized path 로 처리 가능 → Application Service (Use Case) 를 무조건 통과할 필요가 없음을 지지. 단 engineering-blog 등급이므로 official-vendor-doc 이나 official-standard 대비 낮은 신뢰도. - HGRACA-CQRS-C4 에서 "Controller 가 Query object 에 직접 의존" 한다는 설명은 ca-tmpl 의 hexagonal rule (web adapter 가 application layer 를 거쳐야 함) 과 충돌처럼 보이나, Query object 가 application layer 에 위치하면 interface dependency 는 여전히 inward pointing — hexagonal violation 아님 ## Related / 관련 - [[raw/official-docs/cqrs-fowler-bliki]] — CQRS 정의 상위 문서 - [[raw/company-tech-blogs/domain-event-sourcing-vs-cqrs-greg-young]] — CQRS 원작자 Greg Young 의 정의 - [[raw/official-docs/spring-data-jpa-projections-spring-official]] — projection (DTO) 의 Spring 공식 mechanism - [[raw/branch-notes/feature-application-port-usecase-contract]] — QueryUseCase 선행 계약