chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,653 @@
|
||||
# VD-26: Persisted GraphQL operation
|
||||
|
||||
- 상태: Accepted design — reference runtime implementation pending
|
||||
- 결정일: 2026-07-28
|
||||
- provider-neutral GraphQL reference adapter:
|
||||
`DESIGNED_NOT_IMPLEMENTED`
|
||||
- product GraphQL composition: `NOT_SELECTED`
|
||||
- batching/subscription/`@defer`/`@stream`: `NOT_SELECTED`
|
||||
- normalized GraphQL cache: `NOT_SELECTED`
|
||||
- 관련 결정: VD-13, VD-23, VD-24, VD-25, VD-28
|
||||
- 상세 설계:
|
||||
[API contract, Schema, Mapper와 Server State](../api-contract-schema-mapper-and-server-state.md)
|
||||
|
||||
## 배경
|
||||
|
||||
현재 source, package direct dependency, config와 test에는 GraphQL runtime,
|
||||
operation document/codegen, persisted manifest나 endpoint provider가 없다.
|
||||
lockfile의 transitive `graphql` package는 MSW 개발 의존성일 뿐 capability
|
||||
구현 증거가 아니다.
|
||||
|
||||
GraphQL은 임의 query string을 보내는 범용 API escape hatch로 도입하지 않는다.
|
||||
제품이 여러 backend aggregate를 화면별 shape로 조회해야 하고 schema/router,
|
||||
field authorization, persisted allowlist와 cost budget을 운영할 수 있을 때만
|
||||
bounded-context operation family로 선택한다.
|
||||
|
||||
## 결정
|
||||
|
||||
### 1. Production GraphQL은 persisted operation only다
|
||||
|
||||
```text
|
||||
semantic application query/command
|
||||
-> registered GraphqlOperationDefinition
|
||||
-> fixed endpoint
|
||||
-> persisted operation ID/hash
|
||||
-> validated variables
|
||||
-> bounded GraphQL response decoder
|
||||
-> operation data schema
|
||||
-> boundary mapper
|
||||
-> application projection
|
||||
-> TanStack Query or command result
|
||||
```
|
||||
|
||||
production runtime은 다음을 받지 않는다.
|
||||
|
||||
- arbitrary GraphQL document
|
||||
- caller-provided operation name/hash
|
||||
- arbitrary endpoint/header
|
||||
- generated SDK selection set builder
|
||||
- field/fragment string
|
||||
|
||||
### 2. Operation artifact
|
||||
|
||||
```text
|
||||
PersistedGraphqlOperationV1
|
||||
protocol = PERSISTED_GRAPHQL_V1
|
||||
semanticOperationId
|
||||
operationName
|
||||
operationKind = QUERY | MUTATION
|
||||
canonicalDocumentSha256
|
||||
persistedOperationId
|
||||
schemaArtifactId
|
||||
schemaDigest
|
||||
variablesSchemaId
|
||||
dataSchemaId
|
||||
mapperId
|
||||
errorProfileId
|
||||
partialDataPolicy
|
||||
endpointId
|
||||
graphqlHttpProfileRevision
|
||||
persistedEnvelopeProfileId
|
||||
responseStatusMediaProfileId
|
||||
authProfileId
|
||||
csrfProfileId
|
||||
replayPolicy
|
||||
deadlineProfileId
|
||||
retryProfileId
|
||||
serverStateProfileId | null
|
||||
maxVariablesBytes
|
||||
maxResponseBytes
|
||||
maxErrorCount
|
||||
maxCost
|
||||
maxDepth
|
||||
maxAliases
|
||||
owner
|
||||
```
|
||||
|
||||
canonical document는 build artifact이고 runtime string이 아니다. stable operation
|
||||
ID와 hash는 schema/operation manifest에 binding한다.
|
||||
|
||||
manifest 생성:
|
||||
|
||||
```text
|
||||
authenticated immutable schema
|
||||
-> named operation sources
|
||||
-> parse/validate against schema
|
||||
-> canonical document
|
||||
-> operation hash/ID
|
||||
-> variables/result type generation
|
||||
-> runtime codec manifest
|
||||
-> mapper/query profile binding
|
||||
-> persisted operation manifest
|
||||
```
|
||||
|
||||
### 3. Schema와 codegen
|
||||
|
||||
- schema source URL에서 normal build마다 latest를 받지 않는다.
|
||||
- authenticated explicit update workflow가 immutable artifact와 provenance를
|
||||
저장한다.
|
||||
- schema/source/operation manifest digest를 release contract set에 binding한다.
|
||||
- anonymous operation, duplicate operation name와 invalid fragment를 거절한다.
|
||||
- generator, plugins, Node와 runtime version을 pin한다.
|
||||
- clean checkout regenerate diff가 0이어야 한다.
|
||||
- schema breaking diff, operation validation, deprecated field budget와 generated
|
||||
output digest를 CI gate로 둔다.
|
||||
- generated type은 adapter-private DTO다.
|
||||
- generated TypeScript type만 믿지 않고 variables/data runtime codec과 mapper를
|
||||
유지한다.
|
||||
- schema introspection을 production에서 끄는 결정은 server 보안 옵션일 뿐
|
||||
authorization/cost control을 대체하지 않는다.
|
||||
|
||||
### 4. Endpoint와 HTTP profile
|
||||
|
||||
```text
|
||||
GraphqlProviderProfile
|
||||
endpointId
|
||||
fixedHttpsUrl
|
||||
graphqlHttpProfileRevision
|
||||
persistedEnvelopeProfileId
|
||||
methodPolicy
|
||||
credentialsMode
|
||||
corsProfile
|
||||
referrerPolicy
|
||||
redirect = ERROR
|
||||
mediaProfile
|
||||
```
|
||||
|
||||
GraphQL-over-HTTP draft를 움직이는 implicit `latest`로 구현하지 않는다. selected
|
||||
revision의 request/response/status 규칙과 provider의 persisted-operation
|
||||
extension을 immutable profile/fixture에 고정한다. persisted ID/hash-only envelope는
|
||||
표준 request의 required `query` field를 생략하는 provider extension일 수 있으므로
|
||||
generic GraphQL-over-HTTP compliance로 가장하지 않는다.
|
||||
|
||||
private query와 mutation은 POST가 기본이다.
|
||||
|
||||
GET은 다음을 모두 만족하는 public read profile에서만 선택한다.
|
||||
|
||||
- persisted ID/hash와 non-sensitive bounded variables
|
||||
- URL byte ceiling
|
||||
- no credential/private representation 또는 명시된 safe cache contract
|
||||
- exact cache key/Vary/CDN policy
|
||||
- mutation 아님
|
||||
|
||||
raw document와 sensitive variables를 URL에 넣지 않는다.
|
||||
|
||||
request `Content-Type: application/json`과
|
||||
`Accept: application/graphql-response+json`을 기본 exact profile로 둔다.
|
||||
`application/json` response 지원은 legacy provider profile로 분리한다. caller가
|
||||
`fetch` option, headers와 credentials를 override하지 않는다.
|
||||
|
||||
status/media matrix:
|
||||
|
||||
- final URL/origin과 media/body ceiling을 먼저 확인한다.
|
||||
- `application/graphql-response+json`은 profile이 허용한 HTTP status 전체에서
|
||||
bounded GraphQL envelope를 먼저 decode하고 selected revision의 status/body
|
||||
불변조건을 교차 검증한다.
|
||||
- non-null `data`가 있는 response는 selected revision이 요구하는 2xx여야 한다.
|
||||
no-data/error와 partial response의 status는 pinned revision/provider fixture와
|
||||
exact match해야 한다.
|
||||
- legacy `application/json`은 허용된 2xx body만 GraphQL envelope로 신뢰한다.
|
||||
non-2xx body는 intermediary일 수 있으므로 GraphQL error/extensions로
|
||||
해석하지 않고 bounded generic HTTP failure로 닫는다.
|
||||
|
||||
### 5. Request envelope
|
||||
|
||||
wire shape는 provider의 persisted-envelope extension이 versioned codec으로
|
||||
고정한다. 최소 의미:
|
||||
|
||||
```text
|
||||
protocol
|
||||
persisted operation ID
|
||||
canonical document hash
|
||||
operation name
|
||||
validated variables
|
||||
client contract manifest version
|
||||
```
|
||||
|
||||
full document는 포함하지 않는다.
|
||||
|
||||
provider가 ID/hash-only envelope를 지원하지 않으면 이 capability를 그 endpoint에
|
||||
compose하지 않는다. production에서 표준 `query` field를 채우기 위해 full
|
||||
document fallback을 보내는 것으로 우회하지 않는다.
|
||||
|
||||
variables:
|
||||
|
||||
- request runtime schema의 parsed output만 사용
|
||||
- unknown field 거절
|
||||
- depth/node/string/list/encoded byte ceiling
|
||||
- File/Blob/stream/native/generated class 금지
|
||||
- ID/decimal/int64/time semantics는 VD-24
|
||||
- secret/credential를 variable로 전달하는 operation 금지
|
||||
|
||||
### 6. APQ와 manifest miss
|
||||
|
||||
runtime Automatic Persisted Query negotiation을 production default로 사용하지
|
||||
않는다.
|
||||
|
||||
```text
|
||||
persisted miss/hash mismatch
|
||||
-> body/reader cancel
|
||||
-> PERSISTED_OPERATION_MISMATCH
|
||||
-> operation traffic disable or coherent manifest recovery
|
||||
```
|
||||
|
||||
hash miss 뒤 full document를 자동 전송하면 server allowlist와 cost governance를
|
||||
우회할 수 있다. trusted development profile에서만 explicit opt-in 가능하며
|
||||
production promotion 증거로 사용하지 않는다.
|
||||
|
||||
frontend manifest와 router manifest의 N/N-1 rollout을 먼저 증명한다.
|
||||
|
||||
### 7. Total deadline, cancellation과 retry
|
||||
|
||||
VD-23 common logical deadline을 사용한다.
|
||||
|
||||
- credential/CSRF attach
|
||||
- network attempts/backoff
|
||||
- response read/JSON parse
|
||||
- GraphQL envelope/data/error validation
|
||||
- mapper
|
||||
|
||||
Query retry와 GraphQL transport retry를 중복하지 않는다.
|
||||
|
||||
retry:
|
||||
|
||||
- idempotent query의 selected network/408/429/502/503/504
|
||||
- keyed mutation은 backend idempotency evidence가 있을 때만
|
||||
- GraphQL validation, persisted miss, cost/depth, schema/data/error mismatch는
|
||||
retry하지 않음
|
||||
- HTTP 200 GraphQL business error를 transient network failure로 자동 retry하지 않음
|
||||
- UNAUTHENTICATED recovery는 safe query/keyed mutation만 same logical binding으로
|
||||
한 번
|
||||
|
||||
AbortSignal은 fetch와 body/incremental reader를 cancel한다. local cancel이 mutation
|
||||
미적용을 의미하지 않으며 ambiguous effect는 status/reconcile contract로 닫는다.
|
||||
|
||||
### 8. Response decoder
|
||||
|
||||
```text
|
||||
HTTP response
|
||||
-> final URL/origin/media/header
|
||||
-> present/valid Content-Length advisory preflight
|
||||
-> bounded stream reader
|
||||
-> decoded byte/depth/node/string/list cap
|
||||
-> GraphQL response envelope
|
||||
-> pinned HTTP status/body matrix
|
||||
-> data/errors state machine
|
||||
-> operation data codec
|
||||
-> mapper
|
||||
```
|
||||
|
||||
top-level:
|
||||
|
||||
```text
|
||||
GraphqlResponse
|
||||
data?
|
||||
errors?
|
||||
extensions?
|
||||
```
|
||||
|
||||
unknown top-level/extension behavior는 provider profile과 VD-24 unknown-field
|
||||
정책을 따른다. response body, error message, path와 extensions를 log에 복사하지
|
||||
않는다.
|
||||
|
||||
### 9. Data/error state machine
|
||||
|
||||
다음 순서로 배타적으로 처리한다.
|
||||
|
||||
1. network/final URL/unsupported media/body limit 실패 또는 legacy
|
||||
`application/json` non-2xx:
|
||||
transport 또는 media/limit failure, data/cache write 0.
|
||||
2. `application/graphql-response+json`은 profile이 허용한 status 전체에서,
|
||||
legacy `application/json`은 profile-admitted 2xx에서만 bounded parse한다.
|
||||
top-level response shape 불일치는 `GRAPHQL_ENVELOPE_MISMATCH`.
|
||||
3. `errors` key가 있으면 non-empty list여야 한다. `errors=[]`는 항상
|
||||
`GRAPHQL_ENVELOPE_MISMATCH`다.
|
||||
4. selected GraphQL-over-HTTP revision의 status/body matrix가 맞지 않으면
|
||||
`GRAPHQL_HTTP_PROFILE_MISMATCH`다.
|
||||
5. `data` key 존재 + non-null, `errors` 없음:
|
||||
data codec → mapper → generation fence → success.
|
||||
6. `data` 없음/null, non-empty `errors`:
|
||||
safe error mapping; success/cache write 0.
|
||||
7. non-null `data`와 non-empty `errors` 동시:
|
||||
operation `partialDataPolicy` 적용.
|
||||
8. `data` 없음/null이고 errors도 없음:
|
||||
contract mismatch.
|
||||
|
||||
### 10. Error projection
|
||||
|
||||
GraphQL error는 untrusted다.
|
||||
|
||||
```text
|
||||
GraphqlError
|
||||
message
|
||||
locations
|
||||
path
|
||||
extensions
|
||||
```
|
||||
|
||||
application에 허용:
|
||||
|
||||
- operation error profile이 allowlist한 `extensions.code`
|
||||
- bounded typed validation field issue
|
||||
- effect certainty/conflict category
|
||||
- bounded server request/trace ID projection
|
||||
|
||||
금지:
|
||||
|
||||
- raw `message`
|
||||
- source location
|
||||
- path actual value
|
||||
- arbitrary extensions
|
||||
- resolver/service/stack/database detail
|
||||
|
||||
error count, path segment/count/string와 extensions decoded byte cap을 적용한다.
|
||||
unknown code는 generic closed failure다. backend `retryable` boolean은 retry
|
||||
authority가 아니다.
|
||||
|
||||
common mapping 예:
|
||||
|
||||
| safe GraphQL category | AppFailure |
|
||||
| --- | --- |
|
||||
| unauthenticated | `AUTH_REQUIRED` |
|
||||
| forbidden | `FORBIDDEN` |
|
||||
| not found | `NOT_FOUND` 또는 existence-hiding policy |
|
||||
| validation | `VALIDATION_REJECTED` |
|
||||
| conflict/precondition | `CONFLICT` 또는 typed precondition |
|
||||
| rate limited | `RATE_LIMITED` |
|
||||
| internal/unavailable | `SERVER_FAILURE` |
|
||||
| unknown | `UNKNOWN_CLIENT_FAILURE` 또는 contract failure |
|
||||
|
||||
### 11. Partial data
|
||||
|
||||
default:
|
||||
|
||||
```text
|
||||
partialDataPolicy = REJECT
|
||||
```
|
||||
|
||||
query에만 다음 explicit profile을 허용할 수 있다.
|
||||
|
||||
```text
|
||||
ALLOW_TYPED_PARTIAL
|
||||
requiredCompletePaths
|
||||
optionalPartialPaths
|
||||
errorCodeAllowlist
|
||||
completenessSchemaId
|
||||
staleVisibilityPolicy
|
||||
```
|
||||
|
||||
조건:
|
||||
|
||||
- data codec이 missing/null path를 정확히 표현
|
||||
- mapper가 completeness를 application result로 투영
|
||||
- UI가 complete success와 partial-degraded를 구분
|
||||
- partial value/result size ceiling
|
||||
- authorization/error path를 숨기며 unsafe field를 사용하지 않음
|
||||
- previous complete cache와 field 단위로 임의 merge하지 않음
|
||||
|
||||
mutation은 errors가 있으면 partial success data를 ordinary command success로
|
||||
cache하지 않는다. backend가 effect certainty/receipt를 제공해야
|
||||
`COMMITTED | NOT_APPLIED | UNKNOWN`을 판단한다. error가 있다는 이유만으로
|
||||
optimistic layer 전체를 즉시 rollback해 다른 commit을 덮지 않는다.
|
||||
|
||||
### 12. Null bubbling
|
||||
|
||||
GraphQL nullability propagation은 application null 의미와 다르다.
|
||||
|
||||
- nullable field, error-caused null과 absent partial field를 data/error state
|
||||
machine이 함께 해석
|
||||
- generated type의 `T | null`만으로 cause를 추측하지 않음
|
||||
- operation data codec/mapper가 approved partial path와 error code를 결합
|
||||
- required root/aggregate null은 default failure
|
||||
- unauthorized field null을 stale previous field로 자동 채우지 않음
|
||||
|
||||
### 13. Cache identity
|
||||
|
||||
VD-25 TanStack Query가 기본 sole owner다.
|
||||
|
||||
- query key는 semantic operation input + VD-13 scope
|
||||
- persisted operation ID/hash/document를 key에 넣지 않음
|
||||
- GraphQL data/envelope/generated type을 cache하지 않음
|
||||
- mapped bounded application projection만 cache
|
||||
- schema/mapper meaning change는 query/release epoch invalidation
|
||||
- GraphQL client library cache는 disabled/`no-cache`
|
||||
|
||||
normalized cache가 필요하면 separate ADR:
|
||||
|
||||
- key fields/`__typename`
|
||||
- fragment completeness
|
||||
- pagination merge
|
||||
- optimistic layers
|
||||
- eviction/gc/logout/scope
|
||||
- persistence/SSR
|
||||
- TanStack replacement/removal
|
||||
|
||||
dual cache는 금지한다.
|
||||
|
||||
### 14. Batching
|
||||
|
||||
현재 `NOT_SELECTED`.
|
||||
|
||||
`@defer`/`@stream`은 한 GraphQL HTTP operation의 finite incremental response다.
|
||||
장기 subscription이나 unsolicited realtime event가 아니며, reconnect/resume
|
||||
owner를 realtime runtime에 넘기지 않는다.
|
||||
|
||||
선택 조건:
|
||||
|
||||
- 같은 endpoint/auth/scope
|
||||
- query only
|
||||
- same credentials/CSRF policy
|
||||
- max operation count
|
||||
- total variables/request bytes
|
||||
- total cost/depth
|
||||
- per-operation deadline/result/error/observation 보존
|
||||
- one operation cancel/failure가 다른 operation semantics를 바꾸지 않음
|
||||
|
||||
금지:
|
||||
|
||||
- mutation 포함
|
||||
- query+mutation mixed batch
|
||||
- 서로 다른 account/session
|
||||
- batching으로 idempotency/retry owner 합치기
|
||||
- one HTTP result를 one query cache value로 저장
|
||||
|
||||
batch transport failure와 per-operation GraphQL failure를 분리한다.
|
||||
|
||||
### 15. Incremental `@defer`/`@stream`
|
||||
|
||||
현재 `NOT_SELECTED`.
|
||||
|
||||
선택 시 별도 profile:
|
||||
|
||||
- exact incremental-delivery draft/provider revision
|
||||
- exact `Accept`, response `Content-Type`와 boundary/version parameter
|
||||
- exact `multipart/mixed` media/boundary parser
|
||||
- total bytes/parts/depth/patch count
|
||||
- initial/subsequent/terminal payload discriminant와 completion grammar
|
||||
- operation-owned ID/label/path allowlist와 path progression
|
||||
- patch/data/items/errors/extensions runtime schema
|
||||
- part별 및 cumulative error/extension count/byte ceiling
|
||||
- duplicate/out-of-order/missing path
|
||||
- terminal marker
|
||||
- idle/total deadline
|
||||
- backpressure/cancel/reader cleanup
|
||||
- proxy/CDN buffering conformance
|
||||
|
||||
cache:
|
||||
|
||||
- staging projection에 immutable patch 적용
|
||||
- terminal integrity/completeness 뒤 atomic commit
|
||||
- 또는 UI가 explicit progressive state를 소유
|
||||
- existing cached object를 in-place mutate하지 않음
|
||||
- truncated stream을 complete success로 cache하지 않음
|
||||
|
||||
Chromium/Firefox/WebKit과 actual proxy 증거 없이는 traffic promotion 금지다.
|
||||
exact protocol revision/profile이 없으면 registry composition 자체를 거절한다.
|
||||
|
||||
### 16. Subscription
|
||||
|
||||
GraphQL HTTP query adapter에 subscription을 넣지 않는다. 현재 `NOT_SELECTED`.
|
||||
|
||||
선택 시 transport-specific registered GraphQL subscription capability와
|
||||
feature-owned `FeatureEventInput`이 필요하다. 범용 `RealtimePort`를 만들지
|
||||
않는다.
|
||||
|
||||
```text
|
||||
GraphqlSubscriptionCapability
|
||||
subscribe(registered subscription, validated variables, signal)
|
||||
-> AsyncIterable<Result<MappedEvent>>
|
||||
-> unsubscribe()
|
||||
```
|
||||
|
||||
선택된 WebSocket/SSE subprotocol adapter가 frame, media, auth, reconnect/resume를
|
||||
소유하고 GraphQL event schema와 pure mapper를 통과한 event만
|
||||
`FeatureEventInput` 또는 invalidation bridge로 전달한다. backend contract가
|
||||
명시적으로 같은 의미를 채택하지 않는 한 GraphQL payload를
|
||||
`REALTIME_EVENT_V1`로 강제하거나 다시 감싸지 않는다.
|
||||
|
||||
backend 계약:
|
||||
|
||||
- exact WebSocket/SSE protocol/version
|
||||
- auth attach/refresh/revoke
|
||||
- heartbeat/idle timeout
|
||||
- reconnect/backoff
|
||||
- sequence/duplicate/gap/resume cursor
|
||||
- bounded queue/overflow
|
||||
- logout/route unmount unsubscribe
|
||||
|
||||
event는 invalidation hint 또는 registered bounded reducer를 통해 server-state를
|
||||
갱신한다. raw event history를 Query cache에 무한 적재하지 않는다.
|
||||
|
||||
### 17. Authorization, CSRF와 DoS
|
||||
|
||||
- BFF/router가 field/resource authorization을 매 request에 수행
|
||||
- persisted allowlist는 authorization이 아님
|
||||
- cookie mutation은 POST + exact Origin/Fetch Metadata + approved CSRF proof
|
||||
- SameSite/custom header/preflight 단일 요소만 방어라고 주장하지 않음
|
||||
- cross-origin credential wildcard 금지
|
||||
- server에서 depth, aliases, fragments, variables/list/page/field cost, total
|
||||
execution와 response bytes 강제
|
||||
- frontend ceiling은 server DoS 방어를 대체하지 않음
|
||||
- introspection off는 field authorization/cost control 대체 아님
|
||||
- persisted operation manifest와 field authorization change를 coherent rollout
|
||||
|
||||
### 18. Backend/router 계약
|
||||
|
||||
provider가 제공:
|
||||
|
||||
- immutable schema artifact/provenance
|
||||
- persisted operation registration/lookup
|
||||
- exact operation hash/schema digest binding
|
||||
- N/N-1 manifest window와 retirement
|
||||
- cost/depth/alias/list/response budget enforcement
|
||||
- stable safe error code vocabulary
|
||||
- partial/null/effect certainty semantics
|
||||
- idempotency/conflict/revision
|
||||
- auth/CSRF/CORS
|
||||
- request/trace projection
|
||||
- kill switch와 per-operation traffic
|
||||
|
||||
frontend manifest echo만으로 등록/authorization을 승인하지 않는다. router가
|
||||
server-owned manifest에서 operation binding을 재계산한다.
|
||||
|
||||
### 19. Observability
|
||||
|
||||
허용:
|
||||
|
||||
- semantic operation ID/persisted profile ID
|
||||
- schema/manifest compatibility outcome
|
||||
- full/partial/rejected/transport outcome
|
||||
- safe GraphQL error category
|
||||
- cost/depth/variables/response/error/part count bucket
|
||||
- duration/deadline/retry/auth recovery bucket
|
||||
- cache hit/stale/admission outcome
|
||||
|
||||
금지:
|
||||
|
||||
- document/hash actual value
|
||||
- variables/data
|
||||
- raw error message/path/extensions
|
||||
- field/resolver name high-cardinality label
|
||||
- account/resource/cursor/revision
|
||||
|
||||
server가 resolver-level telemetry를 소유한다. browser가 raw field trace를 수집하지
|
||||
않는다.
|
||||
|
||||
### 20. Testing
|
||||
|
||||
build/contract:
|
||||
|
||||
- schema source provenance/digest
|
||||
- schema lint/breaking/deprecation budget
|
||||
- named operation validation
|
||||
- canonical hash/manifest determinism
|
||||
- clean codegen diff
|
||||
- generated import boundary
|
||||
- variables/data codec parity
|
||||
- N/N-1 persisted manifest and retirement
|
||||
|
||||
runtime:
|
||||
|
||||
- unknown/hash mismatch, full-document fallback 0
|
||||
- variables depth/node/string/list/byte cap
|
||||
- GraphQL HTTP revision/media/status-body matrix와 legacy intermediary body
|
||||
- HTTP/media/body cap
|
||||
- all data/errors state branches
|
||||
- empty errors와 null/absent data matrix
|
||||
- error count/path/extensions cap/redaction
|
||||
- null bubbling
|
||||
- partial allowed/rejected/completeness
|
||||
- timeout/cancel/retry/auth recovery
|
||||
- mutation effect certainty/idempotency
|
||||
- scope/generation late result
|
||||
- cache admission/write 0 on failure
|
||||
|
||||
optional:
|
||||
|
||||
- batching mixed/mutation/limit rejection
|
||||
- multipart boundary/truncated/duplicate/out-of-order/terminal
|
||||
- subscription ordering/reconnect/resume/logout
|
||||
|
||||
provider/browser:
|
||||
|
||||
- actual BFF/router allowlist/cost/auth/CSRF/CORS
|
||||
- manifest rollout/retirement
|
||||
- proxy/CDN media/body behavior
|
||||
- Chromium/Firefox/WebKit for selected incremental/subscription capability
|
||||
|
||||
### 21. Rollout
|
||||
|
||||
1. product owner가 GraphQL이 필요한 bounded operation family를 승인한다.
|
||||
2. schema/router/manifest owner와 endpoint/auth/cost/error contract를 확정한다.
|
||||
3. provider-neutral codec/adapter/fake를 구현한다.
|
||||
4. generated source, boundary mapper와 TanStack query definition을 연결한다.
|
||||
5. REST current read와 GraphQL shadow read를 비교하되 shadow result는 UI/cache에
|
||||
쓰지 않는다.
|
||||
6. actual router conformance를 통과한다.
|
||||
7. `AVAILABLE_NOT_COMPOSED`에서 product composition behind
|
||||
`TrafficAdmission=DISABLED`로 이동한다.
|
||||
8. read-only internal canary 뒤 selected operation만 traffic을 올린다.
|
||||
9. mutation은 idempotency/effect certainty provider evidence 뒤 별도 canary한다.
|
||||
10. batching/incremental/subscription은 계속 `NOT_SELECTED` 또는 독립 gate다.
|
||||
|
||||
rollback:
|
||||
|
||||
- 신규 GraphQL operation admission 중지
|
||||
- in-flight query cancel, mutation effect reconcile
|
||||
- current scope GraphQL-mapped query cache clear
|
||||
- coherent frontend/schema/manifest/router rollback
|
||||
- approved REST read fallback이 있으면 새 logical read로 전환
|
||||
- arbitrary/full-document fallback 금지
|
||||
|
||||
### 22. Removal
|
||||
|
||||
1. operation traffic/registration retirement 시작
|
||||
2. query/subscription cancel과 mutation reconcile
|
||||
3. Query cache/invalidation listener clear
|
||||
4. operation/codec/mapper/query profile 제거
|
||||
5. generated files, GraphQL runtime/codegen dependencies 제거
|
||||
6. schema/operation manifest/config/endpoint 제거
|
||||
7. router persisted entries는 N/N-1 window 뒤 제거
|
||||
8. production module/dependency/SBOM/removal test 통과
|
||||
|
||||
## 규범 기준
|
||||
|
||||
- [GraphQL Specification, September 2025](https://spec.graphql.org/September2025/)
|
||||
- [GraphQL over HTTP draft](https://graphql.github.io/graphql-over-http/draft/)
|
||||
|
||||
GraphQL-over-HTTP 문서는 현재 draft이므로 링크의 moving text를 production
|
||||
profile로 쓰지 않고 위에서 결정한 revision/provider fixture로 고정한다.
|
||||
|
||||
## 완료 기준
|
||||
|
||||
- production에서 registered persisted operation 외 document가 전송되지 않는다.
|
||||
- schema/operation/codegen/runtime codec/mapper manifest가 digest로 연결된다.
|
||||
- variables/response/errors가 bounded runtime validation을 거친다.
|
||||
- persisted envelope extension과 GraphQL-over-HTTP revision/media/status matrix가
|
||||
actual router profile에 고정된다.
|
||||
- data/errors/partial/null/effect certainty 상태가 배타적으로 닫힌다.
|
||||
- auth/CSRF/cost/field authorization과 manifest N/N-1을 actual router에서 증명한다.
|
||||
- GraphQL SDK normalized cache와 TanStack dual cache가 없다.
|
||||
- query key/cache에 document/hash/envelope/generated DTO가 없다.
|
||||
- batching/incremental/subscription은 선택 전 설치되지 않는다.
|
||||
- kill switch, rollback과 dependency/manifest removal drill이 통과한다.
|
||||
Reference in New Issue
Block a user