--- kind: CONCEPT slug: application-core-c01 title: application-core가 아는 유일한 프로젝트 의존은 shared-contract다 topic: query-and-pagination-models project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: concept:application-core-c01 evidenceCapturedOn: 2026-09-01 assets: - key: application-core-c01 file: ../../../final/evidence/rendered/application-core-c01.svg - key: application-core-c01-diagram file: ../../../final/assets/diagrams/application-core-c01.svg evidence: - ../../../final/evidence/raw/application-core-c01.txt source: - 원본 분석 절은 final/document.md#a03#L58 이다. module: application-core --- # application-core가 아는 유일한 프로젝트 의존은 shared-contract다 `build.gradle`의 production project dependency는 `:shared-contract` 하나뿐이다. 실행 정책은 use case 타입이 아니라 `@UseCaseCapability`에 선언되고, `CleanArchitectureTest`가 그 선언과 실제 호출의 일치를 검사한다. ## 관계 - **legacy storage/notification compatibility surface의 제거 조건 추적** 같은 분석 리프에서 끌어낸 규칙이다. ## 본문 **Observed.** `build.gradle`의 production project dependency는 `:shared-contract` 하나뿐이다. application-core가 Spring, JPA, Redis, Kafka, filesystem provider 같은 구현 모듈을 직접 참조하지 않고, 외부 구현은 composition root와 adapter가 역으로 이 모듈의 port를 구현한다. ## 의존이 흐르는 방향 :::evidence key="application-core-c01-diagram" alt="어댑터와 application-core 와 shared-contract 가 위에서 아래로 쌓이고 의존 방향 화살표가 아래쪽 하나로만 그려진 구조" caption="의존이 흐르는 한 방향" zoom="false" ::: ## 실행 정책은 애너테이션에 따로 선언된다 `CommandUseCase`와 `QueryUseCase`는 `UseCase.handle(I)`를 write/read intent에 맞게 타입으로 좁힌다. 자체적으로 transaction을 열거나 security interceptor를 실행하지 않는다. 실행 정책은 `@UseCaseCapability`에 별도로 선언된다 — runtime TYPE annotation이며 `transactionMode`, `idempotency`, `repositoryAccess`를 필수로 받고 `externalOutboundAllowed`, `sensitiveRead`, `bulkWrite`, `crossTenantAdmin`을 추가 선언한다. ## CleanArchitectureTest 참조 위치 :::evidence key="application-core-c01" alt="코드베이스에서 CleanArchitectureTest 를 검색한 출력 6줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="CleanArchitectureTest 코드베이스 검색 — 6줄 · exit 0" zoom="true" ::: ## 적합성 함수가 검사하는 일곱 가지 annotation 자체는 metadata에 불과하지만 `CleanArchitectureTest`가 concrete Command/Query use case에 annotation 존재를 강제한다. 그 위에서 architecture fitness function이 다음 coherence를 직접 검사한다. - `READ_ONLY + READ_REPOSITORY`는 `TransactionPort.inRead`를 직접 호출해야 한다. - `WRITE + WRITE_REPOSITORY`는 `inWrite` 또는 `inRootWrite`를 직접 호출해야 한다. - `REQUIRES_NEW`는 `inNew`를 직접 호출해야 한다. - `repositoryAccess != WRITE_REPOSITORY`인 use case가 repository write verb를 직접 호출하면 실패한다. - `bulkWrite=true`는 `WRITE_REPOSITORY`를 요구한다. - mutating use case는 type-level `@RequiresPermission`을 선언해야 한다. - application/domain은 Spring Security에 의존할 수 없다. ## 이 강제가 잡지 못하는 것 이 enforcement에는 의도적으로 한계가 있다. ArchUnit의 direct-call 분석이므로 helper 뒤에 숨은 repository mutation/transaction call은 잡지 못하고, AOP self-invocation/non-bean path도 static rule만으로 보장하지 않는다. 이 제한은 테스트 설명 자체에 명시돼 있어 최종 계약의 일부로 봐야 한다.