feat: jpa, messaging, notification, mongo, graphql 어댑터터 구현체 추가

This commit is contained in:
DongHyeonka
2026-08-15 13:01:58 +09:00
parent ac874e49e6
commit 2f5d2fc219
909 changed files with 62510 additions and 6354 deletions
+43 -8
View File
@@ -72,11 +72,40 @@ gRPC 와 달리 spring-graphql / graphql-java 는 Spring Boot BOM 이 관리한
버전 명시도, 모듈 스코프 platform import 도 필요 없다 — `build.gradle` 은 BOM-managed 좌표만
선언하고, per-module `gradle.lockfile` 이 strict locking 으로 정확한 버전을 고정한다.
## 설정 — 프레임워크 `spring.graphql.*`
## 설정 — 프레임워크 `spring.graphql.*` + 플랫폼 `backend.graphql.*`
이 모듈은 자체 `@ConfigurationProperties` 를 두지 않는다. path, graphiql, introspection, schema
location 은 프레임워크 `spring.graphql.*` composition-root `application.yml` 에서 설정한다
(모듈별 `yml` 없음). 정말 필요한 knob 이 생기기 전까지 커스텀 설정 클래스는 두지 않는다.
전송 계층 설정(path, graphiql, introspection, schema location)은 프레임워크 `spring.graphql.*`
가 소유한다. composition-root `application.yml` 에서 설정하며 모듈별 `yml` 은 없다.
플랫폼 정책은 `spring.graphql.*` 로 표현할 수 없다 — 실행 프로파일, cost/page 한계, preparsed
캐시 경계, cursor 키 링, 관측 label 로 허용할 operation 이름은 전부 이 leaf 의 결정이다. 그래서
`GraphQlPlatformProperties`**`backend.graphql`** prefix 로 `@ConfigurationProperties`
바인딩한다(`spring.graphql.platform.*` 이 아니다 — 그 prefix 는 존재한 적이 없다).
```yaml
backend:
graphql:
production: true
environment: PRODUCTION_PUBLIC
execution-profile: BLOCKING_MVC
validation-policy-version: v1 # preparsed 캐시 키의 일부
console:
graphiql-enabled: false
introspection-enabled: false
limits:
maximum-page-size: 100
maximum-complexity: 10000
preparsed-cache-entries: 1000
preparsed-cache-weight: 10000000
preparsed-cache-expire-after-access: 30m
cursor:
key-ids: [cursor-key-1] # 키 자체는 설정에 오지 않는다
observed-operation-names: [] # 비우면 모든 operation 이름이 `other` 로 접힌다
```
`observed-operation-names` 가 비어 있는 것이 기본값이자 안전한 값이다. operation 이름은 문법만
검증될 뿐 개수가 제한되지 않으므로, 원본을 그대로 metric label 로 쓰면 정상 클라이언트 하나가
metrics 백엔드를 무너뜨릴 수 있다(`observation/GraphQlOperationNameCardinality`).
`GraphqlHttpBoundaryQualificationTest` 는 실제 random-port MVC HTTP 서버 위에서 test-only
SecurityFilterChain 과 CORS allowlist 를 조합해 인증, origin, GraphiQL 비활성화, introspection
@@ -109,10 +138,16 @@ composition root 는 이 leaf 를 채택할 때 인증/인가 및 CORS 정책을
분해 비용은 낮게 유지했다.
어느 패턴이든 "패키지는 경계가 아니다"라는 약점은 기계 검증으로 메웠다 —
`build/GraphQlStableModule`·`GraphQlAdvancedModule` 이 모듈 정체성과 허용 edge 를 값으로 선언하고,
`GraphQlModuleBoundaryTest` 가 **실제 소스 트리를 스캔**해 Stable→Advanced import, core-api 의
프레임워크 import, Stable edge 의 Advanced 참조를 실패시킨다. Gradle 이 해주던 일을 테스트가
한다.
`moduleboundary/GraphQlStableModule`·`GraphQlAdvancedModule` 이 모듈 정체성과 허용 edge 를 값으로
선언하고, `GraphQlModuleBoundaryTest` 가 **실제 소스 트리를 스캔**해 Stable→Advanced import,
CORE 모듈의 프레임워크 import, 선언되지 않은 edge, 미등록 패키지를 실패시킨다. Gradle 이
해주던 일을 테스트가 한다.
스캔은 컴파일된 클래스가 아니라 **소스 텍스트**를 읽는다. 경계가 금지하는 import 는 상수
인라이닝이나 미보존 시그니처로 바이트코드에서 지워지는 경우가 많아서, 바이트코드 스캔은
리뷰어가 읽는 소스가 여전히 경계를 넘는데도 clean 이라고 보고한다. 그리고 스캐너는
**파일을 하나도 못 찾으면 통과가 아니라 실패한다** — 0개 스캔으로 green 이 되는 것이 이
모델이 막으려는 실패 그 자체다.
## ArchUnit/JPA 없이 아키텍처 규칙을 강제한 방법