feat: jpa, messaging, notification, mongo, graphql 어댑터터 구현체 추가
This commit is contained in:
@@ -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 없이 아키텍처 규칙을 강제한 방법
|
||||
|
||||
|
||||
Reference in New Issue
Block a user