53 KiB
API contract, Schema, Mapper와 Server State platform
정본 안내 (non-authoritative for runtime capability decisions)
Runtime Config/boot, Fetch HTTP client, Router, Query/Mutation, realtime 공통 경계, Web Worker, Service Worker, offline command와 Background Sync의 구현 결정은 프론트엔드 런타임 Capability 저장소 정합형 구현 결정 폐쇄 상세 설계가 정본이다. 본 문서의 해당 서술이 정본과 충돌하면 정본을 따른다.
- 상태: capability별 current/target 분리, production design accepted
- 기준일: 2026-07-28
- 범위: REST, GraphQL over HTTP, Connect-Web/Connect, gRPC-Web, Protobuf/REST Gateway, runtime Schema, boundary Mapper, TanStack Query 기반 Server State Cache
- 관련 결정:
- browser Protobuf/gateway 상세 설계: Protobuf browser transport와 REST Gateway
- backend handoff: Backend API와 Server State contract
- 운영 절차: API contract와 server-state recovery
1. 목적
이 문서는 다음 질문을 하나의 production 계약으로 닫는다.
- REST, GraphQL, Connect와 gRPC-Web 중 무엇을 어디에 사용하는가
- Protobuf contract와 REST Gateway가 transport/runtime과 어떻게 분리되는가
- request/response가 어느 지점까지 untrusted wire data인가
- TypeScript type, generated code와 runtime validation의 역할은 무엇인가
- DTO를 domain/application projection으로 누가 변환하는가
- server response를 어떤 query identity와 lifecycle로 cache하는가
- schema, mapper, transport와 cache가 바뀔 때 어떻게 배포·관측·rollback하는가
각 protocol은 서로 대체 가능한 URL 호출 문법이 아니다. transport-specific codec, proxy와 failure semantics는 adapter가 소유한다. application은 transport 종류, URL, GraphQL document, protobuf message나 TanStack Query를 직접 알지 않고 feature-owned gateway와 application input만 호출한다.
2. 상태 모델
이 문서는 browser data 설계와 동일한 primary current-status literal을 사용한다.
| primary status | 의미 |
|---|---|
COMPOSED |
production bootstrap 또는 설치된 feature 호출 경로에 concrete runtime이 실제 연결돼 있다. |
AVAILABLE_NOT_COMPOSED |
실행 가능한 reference runtime과 test가 있지만 production graph에는 연결하지 않았다. |
DESIGNED_NOT_IMPLEMENTED |
계약·불변조건·failure와 promotion 기준은 승인됐지만 해당 runtime 또는 필수 orchestration이 없다. |
NOT_SELECTED |
제품 요구, owner와 비용이 승인되지 않아 의도적으로 선택하지 않았다. |
PLATFORM_LIMITED |
target browser/protocol이 요구 semantics를 공통으로 보장하지 못한다. |
primary status와 다음 readiness 축을 섞지 않는다.
Selection
NOT_SELECTED | SELECTED | REMOVING
TrafficAdmission
DISABLED | SHADOW | CANARY | ENABLED
RuntimeHealth
UNKNOWN | AVAILABLE | DEGRADED | UNAVAILABLE | INCOMPATIBLE
PromotionEvidence
MISSING | PARTIAL | COMPLETE | EXPIRED
COMPOSED는 traffic이 켜졌거나 provider가 conformant라는 뜻이 아니다.
AVAILABLE_NOT_COMPOSED도 제품 bundle에 dependency가 들어갔다는 뜻이 아니다.
3. 현재 capability ledger
| capability | primary current status | 현재 증거 | 목표 또는 잔여 |
|---|---|---|---|
| installed REST reference vertical | COMPOSED |
operation registry → request schema → HTTP → envelope/payload schema → mapper → application input → Query 화면 경로 | 아래 REST hardening delta와 실제 제품 provider 계약 |
| shared REST JSON executor | COMPOSED |
path/search/body codec projection, shared deadline/retry-sleep budget, AbortSignal, bounded auth recovery, exact envelope/media/status, safe failure와 diagnostics | 204/304/412 execution join과 actual provider conformance |
| REST v2 security/execution baseline | COMPOSED |
collision-aware operation composition, path placeholder↔codec key exact join, prefix-preserving HTTPS/loopback provider, named bearer/CSRF profile와 credential-mode ceiling, auth fail-before-fetch, bounded JSON, outbound correlation/status·physical-attempt 관측 | cookie-CSRF/CORS provider evidence, 204/304/412 conditional execution과 compatibility artifact |
| GraphQL provider-neutral reference adapter | DESIGNED_NOT_IMPLEMENTED |
source/dependency/codegen/runtime 없음 | persisted-operation-only transport, GraphQL response decoder, mapper binding과 contract harness |
| product GraphQL composition | NOT_SELECTED |
endpoint/schema/persisted manifest/owner 없음 | 제품 query가 REST보다 GraphQL aggregation을 정당화할 때 선택 |
GraphQL batching, subscription, @defer/@stream |
NOT_SELECTED |
없음 | 각각 독립 ADR, proxy/browser lifecycle과 cache semantics 필요 |
| provider-neutral Browser RPC V3 contract/runtime | AVAILABLE_NOT_COMPOSED |
operation/profile/schema/mapper/encoder/transport exact join, typed application port, bounded unary retry/deadline/abort, server-stream idle/total/message/terminal/generation fence와 fail-closed unavailable adapter test | selected descriptor/generated client와 protocol-specific bounded transport를 붙이고 actual provider/browser conformance |
| gRPC-Web unary reference adapter | DESIGNED_NOT_IMPLEMENTED |
source/dependency/generated message 없음 | fixed method registry, protobuf codec, trailers/status/deadline와 proxy conformance |
| gRPC-Web server-stream reference adapter | DESIGNED_NOT_IMPLEMENTED |
source 없음 | bounded frame/idle/total budget, sequence/resume application protocol과 stream port |
| product gRPC-Web composition | NOT_SELECTED |
service descriptor/proxy/owner 없음 | browser-facing gRPC-Web gateway가 실제 이점을 줄 때 선택 |
| gRPC-Web client-streaming/bidi guarantee | PLATFORM_LIMITED |
gRPC-Web browser baseline이 해당 semantics를 제공하지 않음 | REST upload, WebSocket/WebTransport 또는 별도 protocol을 선택 |
| Protobuf schema/codegen governance | DESIGNED_NOT_IMPLEMENTED |
.proto, Buf config, descriptor와 generated output 없음 |
authenticated source, immutable descriptor, deterministic codegen과 compatibility evidence |
| Connect-Web unary/server-stream reference adapter | DESIGNED_NOT_IMPLEMENTED |
@connectrpc/*, @bufbuild/protobuf, generated service와 provider 없음 |
exact Connect/gRPC-Web transport row, bounded decode와 actual browser/provider conformance |
| product Connect protocol composition | NOT_SELECTED |
service/provider/owner 없음 | Protobuf-first backend의 selected browser operation이 있을 때만 선택 |
| Connect browser client-streaming/bidi guarantee | PLATFORM_LIMITED |
Connect protocol 기능과 browser request-stream 지원은 다름 | 별도 duplex/application protocol 선택 |
| Protobuf REST Gateway reference contract/harness | DESIGNED_NOT_IMPLEMENTED |
HttpRule/transcoder/OpenAPI/provider fixture 없음 | selected kind의 deterministic/provider conformance |
| product Protobuf REST Gateway composition | NOT_SELECTED |
route/provider/owner 없음 | curated BFF, grpc-gateway 또는 Envoy transcoder 중 하나와 public HTTP contract 승인 |
| feature runtime request/response Schema | COMPOSED |
Zod request/payload schemas와 installed schema registry가 reference feature에 연결 | byte/depth/node ceiling, unknown-field profile, schema artifact/digest와 multi-protocol source governance |
| schema/mapper v2 reference baseline | COMPOSED |
collision-aware schema codec/mapper contribution install, operation schema/mapper reference resolution, request reject/response strip 방향, bounded collection, typed no-throw mapping result와 operation별 cast-free result guard | actual codec fingerprint, source provenance와 multi-protocol compatibility policy |
| generated contract artifact governance | DESIGNED_NOT_IMPLEMENTED |
generated-api recipe만 있고 generator/provider 선택 없음 |
OpenAPI/GraphQL/proto source authentication, pinned generation, drift/breaking gate와 N/N-1 |
| feature boundary Mapper | COMPOSED |
installed mapper metadata composer와 response-schema exact join 뒤 typed no-throw MappingResult → immutable domain/application view 실행 | numeric/date/null/enum canonical rules와 generated-artifact join |
| TanStack Query memory Server State | COMPOSED |
QueryClient, cancellation, stale-degraded UI, session-generation cancel/clear fence와 invalidation coordinator | account identity projection과 bounded topic↔namespace many-to-many registry |
| reference bound-query/server-state profile | COMPOSED |
bound definition, strict canonical input, scope-private opaque identity, active lease/LRU/collision/entry-byte ceiling, per-profile policy와 result admission | account projection, conditional HTTP execution join과 pagination composition |
| mutation duplicate coordinator baseline | COMPOSED |
QueryClient runtime/scope 단위 exact semantic input identity로 identical만 join하고 distinct input을 합치지 않으며 late scope result를 폐기 | logical-key serialization과 effect certainty/reconcile |
| optimistic ordered-layer runtime | AVAILABLE_NOT_COMPOSED |
out-of-order commit/rollback, authoritative external update 재적용과 expired-scope 제거 test | 제품 mutation의 deterministic membership/revision contract 승인 뒤 definition에 연결 |
| conditional validator CAS sidecar | AVAILABLE_NOT_COMPOSED |
scope/representation/cache revision exact binding, ETag validation과 bounded capacity test; session 전환 clear는 production infrastructure에 연결 | HTTP If-None-Match/304 query transaction과 query removal lifecycle join |
| bounded cursor chain runtime | AVAILABLE_NOT_COMPOSED |
page invariant, cursor loop, snapshot drift, page/item/byte/cursor ceiling과 abort test | backend CursorPage DTO/next cursor 계약 후 reference/infinite-query binding |
| cross-context server-state invalidation | COMPOSED |
singular opaque topic 기반 invalidate-only coordinator와 session-generation local reset | account projection과 bounded topic↔namespace many-to-many registry |
| normalized GraphQL entity cache | NOT_SELECTED |
없음 | TanStack operation-result cache로 해결되지 않는 측정된 요구가 있을 때 별도 선택 |
| persisted query cache | reference DESIGNED_NOT_IMPLEMENTED, product NOT_SELECTED |
Web Storage persistence는 금지, IndexedDB persister 없음 | VD-13의 scope/retention/restore gate를 별도 통과 |
| offline mutation queue | NOT_SELECTED |
foreground mutation만 존재 | backend idempotency/cursor/conflict protocol과 durable command owner 필요 |
현재 REST reference는 Response.json()이 아니라 byte-bounded reader를 사용하고,
external auth owner는 allowlisted header patch만 반환한다. 인증 통합 실패는 fetch
전에 닫히며 correlation, success status와 physical attempt가 terminal observation에
반영된다. 다만 이 baseline을 conditional response, complete pagination,
provider conformance나 GraphQL/gRPC runtime의 증거로 재사용하지 않는다.
마찬가지로 Browser RPC V3 공통 coordinator의 AVAILABLE_NOT_COMPOSED 판정은
vendor wire adapter의 구현 판정이 아니다. @connectrpc/*, official grpc-web,
generated message와 descriptor가 없는 현재 상태에서 Connect/gRPC-Web 각 row는
계속 DESIGNED_NOT_IMPLEMENTED다.
4. 최상위 경계
presentation
-> feature application input
-> feature use case
-> feature gateway port
-> operation registry
-> REST adapter
-> GraphQL adapter
-> Connect adapter
-> gRPC-Web adapter
-> bounded wire decoder
-> runtime schema / semantic validation
-> boundary mapper
-> immutable application projection
-> server-state query adapter
-> registry-owned query identity and policy
-> mapped application result only
금지 경로:
page -> fetch / GraphQL SDK / generated Connect/gRPC client
page -> raw URL / query document / protobuf message
transport DTO -> domain or presentation public type
Response / GraphQL response / generated message -> Query cache
Query cache -> authorization or business conflict authority
application port는 use-case 의미를 표현한다. 예를 들어
listResources(filters), createResource(command)는 허용하지만
executeGraphql(document, variables), grpcCall(service, method, bytes)와
request(url, options)는 허용하지 않는다.
5. Protocol-neutral operation contract
각 외부 호출은 build-time registry의 discriminated row 하나로 고정한다.
application caller는 operationId와 schema가 허용한 input만 제출한다.
ApiOperationContractV3
registryVersion
operationId
owner
protocol = REST | GRAPHQL_HTTP | CONNECT_HTTP | GRPC_WEB
semantics = QUERY | COMMAND | SERVER_STREAM
authProfileId
csrfProfileId
replayPolicy = SAFE | IDEMPOTENT | KEYED_COMMAND | NON_REPLAYABLE
idempotencyKeyPolicy = NONE | REQUIRED
requestSchemaId
responseSchemaId
mapperId
errorProfileId
deadlineProfileId
retryProfileId
serverStateProfileId | null
invalidationTopicRefs[] = { topicId, topicVersion }
dataClassification
compatibility
globalApiContractVersion
protocolArtifactId
minimumServerVersion
retirementEpoch | null
protocolBinding
protocolBinding은 transport별 closed union이다.
REST
method
relativePathTemplate
requestProjection
requestMediaProfile
responseMediaProfile
conditionalProfile
GRAPHQL_HTTP
endpointId
graphqlHttpProfileRevision
persistedEnvelopeProfileId
responseStatusMediaProfileId
persistedOperationId
persistedOperationSha256
operationType
partialDataPolicy
GRPC_WEB
endpointId
clientRuntimeId
grpcWebWireSpecRevision
transportProfile
responseHttpStatusProfileId
fullyQualifiedService
method
rpcKind = UNARY | SERVER_STREAM
requestMessageId
responseMessageId
CONNECT_HTTP
endpointId
clientRuntimeId
connectProtocolRevision
encoding = PROTO_JSON | PROTO_BINARY
requestMethod = POST | GET
fullyQualifiedService
method
rpcKind = UNARY | SERVER_STREAM
descriptorArtifactId
descriptorDigest
registry validation은 다음을 build/boot 전에 거절한다.
- 중복 operation/mapper/profile ID
- protocol과 맞지 않는 binding field
- 등록되지 않은 schema, mapper, auth, deadline, retry와 cache profile
QUERY인데 replay policy가SAFE | IDEMPOTENT가 아님KEYED_COMMAND인데 idempotency key policy가REQUIRED가 아니거나 backend dedupe/reconcile profile이 없음NON_REPLAYABLE인데 network/401 replay가 enabledCOMMAND인데 cache profile이 query data owner로 지정됨SERVER_STREAM인데 ordinary query cache profile을 사용- opening retry가 enabled인데 replay policy가
SAFE | IDEMPOTENT가 아님 KEYED_COMMAND | NON_REPLAYABLEserver stream인데 opening retry가 enabled거나 explicit resume/reconcile/dedupe profile이 없음SERVER_STREAM인데protocol=CONNECT_HTTP | GRPC_WEB과rpcKind=SERVER_STREAM조합이 아님. REST SSE와 GraphQL subscription을 이 registry 의미로 암묵 등록하지 않음- unsafe REST method 또는 GraphQL mutation인데 CSRF/replay/key 결정이 없음
- gRPC-Web client/bidi method
- Connect browser client/bidi method 또는 descriptor의
NO_SIDE_EFFECTS가 없는 Connect GET - absolute URL, runtime GraphQL document 또는 caller-provided service/method
- implementation hard ceiling보다 큰 timeout, byte, frame, page와 retry 값
- GraphQL operation↔provider의 HTTP revision/envelope/status-media profile 또는 Connect/gRPC-Web operation↔provider의 runtime/wire/status/capability tuple mismatch
현재 installed reference operation은 REST v2 metadata를 사용한다. operation, runtime schema codec과 mapper contribution은 object spread가 아니라 각각의 collision-aware composer로 설치되며 duplicate ID를 덮어쓰기 전에 거절한다. boot-time binding 검증은 path placeholder와 codec key, provider/auth/CSRF profile, path/request/response schema와 mapper input schema를 exact resolve한 뒤 immutable registry를 발행한다. GraphQL/Connect/gRPC-Web discriminant와 protocol-specific binding은 해당 reference adapter가 아직 없으므로 source에 구현됐다고 표현하지 않는다.
현재 API_CONTRACT_VERSION은 runtime config와 release manifest의 문자열 일치
gate다. 목표 contract set은 REST/OpenAPI artifact, GraphQL schema/persisted
manifest, protobuf descriptor, runtime schema/mapper registry digest를 포함한
bounded manifest를 만들고 global compatibility version과 함께 release tuple에
binding한다. 문자열 일치만 actual backend compatibility 증거로 사용하지 않는다.
6. 공통 실행 lifecycle
lookup exact operation
-> freeze session/account/runtime generation
-> validate and canonicalize application input
-> derive exact query/command identity
-> allocate total operation deadline
-> encode transport request from registry binding
-> attach credential/CSRF through approved owner
-> execute bounded attempt
-> bounded response/frame decode
-> validate transport envelope/status
-> validate operation DTO/message semantics
-> map to immutable application projection
-> re-check scope/generation
-> return Result
-> query adapter may admit mapped value to memory cache
현재 v2 auth owner는 Request를 반환하지 않고 transport가 만든 immutable
request binding에 대해 allowlisted credential patch만 제공한다. 최소한
transport는 attach 뒤에도 URL, origin, method, body digest, content headers,
idempotency와 conditional binding이 바뀌지 않았음을 다시 검증한다.
auth-required operation은 session state가 unauthenticated/integration-failed이거나
credential attachment가 실패하면 fetch 0회로 닫는다. 동시 401 recovery는
session owner의 single-flight 한 번만 공유하며 replay-safe operation만 동일
logical deadline/idempotency binding으로 한 번 재실행한다.
모든 async boundary와 terminal cache write 전에 captured generation을 확인한다. logout/account switch 뒤 끝난 response, mapper와 stream frame은 old runtime 결과로 폐기한다.
deadline은 attempt마다 새로 시작하지 않는다.
total budget
= credential attach
+ network attempts
+ retry delay
+ body/frame read
+ schema validation
+ mapper
각 phase에 별도 하위 ceiling을 둘 수 있지만 전체 deadline을 늘릴 수 없다. caller abort, runtime teardown, timeout과 provider cancellation은 서로 다른 safe failure로 정규화한다.
7. Transport 선택 기준
| 요구 | 기본 선택 | 이유 |
|---|---|---|
| resource/command, HTTP cache/conditional semantics, 파일 handoff | REST | Web/BFF·CDN·운영 도구와 자연스럽고 failure/status가 명확함 |
| 여러 aggregate를 한 화면 shape로 읽고 client별 selection이 유의미 | persisted GraphQL query | allowlisted operation으로 over/under-fetch를 줄일 수 있음 |
| Protobuf-first backend의 내부 web UI, unary 또는 bounded server stream | Connect-Web/Connect 우선 평가 | generated descriptor와 Fetch 기반 browser RPC를 재사용 |
| 기존 gRPC-Web proxy/conformance 자산 | selected gRPC-Web runtime | runtime별 binary/text/stream capability를 exact profile로 고정 |
| ProtoJSON/HttpRule 자체가 승인된 public HTTP contract | generated REST Gateway 검토 | envelope/status/cache/idempotency를 별도 증명 |
| 현재 REST envelope·ETag·Range·제품 DTO가 중요 | curated REST BFF 유지 | generated transcoder가 제품 HTTP 의미를 자동 제공하지 않음 |
| browser client/bidi streaming | Connect/gRPC-Web 사용 금지 | protocol 자체 기능과 browser 공통 지원을 혼동하지 않음 |
| arbitrary ad-hoc query | GraphQL 사용 금지 | cost, authorization, cache identity와 operation governance를 우회 |
| 단순 CRUD인데 GraphQL/gRPC dependency만 추가 | REST 유지 | 복잡도와 bundle/proxy 비용을 정당화하지 못함 |
한 feature가 여러 protocol을 사용할 수 있지만 한 operationId는 한 protocol에만
binding한다. query read를 shadow 비교하는 경우에도 secondary 결과는 사용자와
cache에 반영하지 않는다. command는 protocol 장애를 이유로 자동 failover/replay
하지 않는다.
8. REST 설계 요약
REST 상세 결정은 VD-23이 소유한다. 공통 baseline은 다음과 같다.
- base origin과 relative path template은 composition/registry가 소유한다.
- provider base URL은 HTTPS와 exact origin/path-prefix를 고정하고 userinfo, query와 fragment를 금지한다. path join은 선택한 base prefix를 보존하며 leading slash가 prefix를 조용히 제거하지 않는다.
- method는 closed union이며 path/search/header/body는 각 runtime schema를 지난다.
- replay semantics는
SAFE,IDEMPOTENT,KEYED_COMMAND,NON_REPLAYABLE로 method와 교차 검증한다. - caller-provided URL, header,
credentials, redirect와 cache option을 금지한다. - cookie session이면 unsafe method에 approved CSRF owner가 필요하다.
SAFE | IDEMPOTENT | KEYED_COMMAND중 exact retry profile과 실제 provider replay evidence가 있는 operation만 network retry한다.- keyed retry와 401 recovery는 같은 logical idempotency key를 유지한다.
- total deadline, attempts, backoff와
Retry-After는 implementation ceiling 안이다. - JSON/error body는 present/valid
Content-Lengthadvisory preflight와 actual decoded-byte bounded stream reader 뒤 parse한다. encoded transfer cap은 BFF/proxy/CDN가 집행한다. - status, content type, response media profile과 envelope 조합을 exact하게 검증한다.
- 204, 304, 412, 422와 problem/envelope profile은 operation이 명시한 경우만 허용한다.
- strong ETag는 cache identity가 아니라 exact representation revalidation
metadata다. validator를 diagnostics에 기록하지 않는다.
Last-Modified는 별도 weak/time validator profile이 승인되기 전 이번 target에 포함하지 않는다. - cursor는 opaque하며 filter/sort/scope와 binding한다. arbitrary URL을
nextlink로 따라가지 않는다. - browser HTTP cache와 application ETag/TanStack revalidation owner 중 하나를 operation별로 선택한다. 두 cache의 freshness를 서로 추측해 합치지 않는다.
- request/response body, URL query, authorization, CSRF, idempotency key와 raw backend copy를 log/telemetry에 넣지 않는다.
9. GraphQL 설계 요약
GraphQL 상세 결정은 VD-26이 소유한다. reference target은 persisted operation only다.
- production bundle은 arbitrary GraphQL document string을 runtime에 받지 않는다.
- build artifact가 operation name, stable ID, SHA-256, variables/result schema, schema digest와 owner를 manifest로 만든다.
- BFF/router는 allowlist에 없는 ID/hash와 cost/depth limit 초과를 거절한다.
- endpoint는 fixed HTTPS registry ID이고 POST가 기본이다.
- selected provider가 지원하는 GraphQL-over-HTTP revision과 persisted-envelope extension을 profile에 고정한다. ID/hash-only request를 generic 표준 envelope로 가장하지 않는다.
Accept: application/graphql-response+json을 우선하고 final URL/media/body cap 뒤에는 허용 HTTP status의 GraphQL envelope를 bounded decode한 다음 status/body matrix를 교차 검증한다. legacyapplication/json은 별도 profile이다.- public cacheable query의 GET은 별도 threat/cache review 뒤에만 허용한다.
- variables는 request schema와 byte/depth/node ceiling을 통과한다.
- HTTP status와 GraphQL
data/errors/extensions를 두 단계로 검증한다. - default
partialDataPolicy=REJECT; 승인 operation만 typed completeness metadata와 함께 partial을 application으로 투영할 수 있다. - error message, path value와 arbitrary extensions를 노출하지 않고 registered
safe code/category만
AppFailure로 mapping한다. - APQ miss에서 full document를 자동 전송하지 않는다. manifest/version mismatch로 fail-closed하고 coherent frontend/router artifact를 복구한다.
- batching은 auth, deadline, cancel, observation과 partial failure owner가
별도 승인되기 전
NOT_SELECTED다. - subscription,
@defer,@stream은 ordinary query adapter에 암묵적으로 넣지 않는다. - Apollo/urql normalized cache는 기본 dependency가 아니다. mapped operation result의 memory owner는 TanStack Query다.
10. Browser Protobuf RPC와 REST Gateway 설계 요약
축과 선택 기준의 상세 계약은 Protobuf browser transport와 REST Gateway가 소유한다. Protobuf는 IDL/serialization, Connect와 gRPC-Web은 browser wire protocol, Connect-Web/official grpc-web은 client runtime, REST Gateway는 HTTP 노출 방식이다. 네 이름을 하나의 대안 목록이나 하나의 auto-negotiating executor로 합치지 않는다.
10.1 gRPC-Web
gRPC-Web 상세 결정은 VD-27이 소유한다. reference target은 unary와 bounded server-stream만 다룬다.
- checked-in/generated artifact는 pinned proto descriptor/module digest에 묶는다.
- vendor generated client/message는 feature adapter 내부에만 존재한다.
- fully-qualified service/method와 endpoint는 registry가 고정한다.
- client runtime과 gRPC-Web wire-spec revision을 operation/provider에 고정한다.
official grpc-web runtime 기준 binary profile은 unary에만 사용하고 server
stream은
grpcwebtext에 binding한다. - Connect-Web의
createGrpcWebTransport()는 Fetch 기반 binary/JSON unary와 server stream profile이며grpcwebtextprofile이 아니다. official XHR runtime의 capability matrix를 이 runtime에 적용하지 않는다. platform-authored custom binary streaming도 세 browser와 actual proxy의 incremental evidence가 있는 별도 runtime profile만 허용한다. - frame header, message length, compression flag, total bytes와 frame count를 bounded decoder가 검증한다.
- HTTP status와 terminal status source를 함께 검사한다. terminal status source는 body trailer frame 또는 zero-body trailers-only response header 중 정확히 하나이며 중복/충돌을 거절한다.
grpc-message, binary error details와 metadata는 allowlist projection 없이 application에 반환하지 않는다.- deadline은
grpc-timeout과 local total deadline의 더 짧은 값이며 AbortSignal이 fetch/stream reader를 cancel한다. - unary
SAFE | IDEMPOTENT | KEYED_COMMAND중 provider evidence가 있는 operation만 retry한다. stream reconnect는 retry가 아니라 server-owned sequence/resume-token을 가진 별도 application protocol이다. - idle deadline, total deadline, max frame/message/count/buffer를 모두 둔다.
- client-streaming/bidi는 지원한다고 가장하지 않는다.
- Envoy/BFF/Connect/gRPC-Web proxy의 CORS, exposed trailers, content type, auth와 maximum message 설정을 actual provider conformance로 검증한다.
- int64/uint64는 JavaScript number로 변환하지 않고 safe integer 범위를 증명하거나 decimal string/adapter-private bigint로 mapping한다.
10.2 Connect-Web과 Connect protocol
Connect 상세 결정은 VD-29가 소유한다.
createConnectTransport()와createGrpcWebTransport()는 같은 package의 서로 다른 wire protocol이다. decoder/status/terminal profile을 공유하지 않는다.- Connect unary의 JSON/binary와 POST를 operation row에 고정한다.
- GET은 unary +
NO_SIDE_EFFECTSdescriptor + non-sensitive bounded input + exact URL/cache/CORS profile에서만 별도 승인한다. - Connect server stream은 final EndStream envelope를 확인하기 전 성공이 아니다.
- stock runtime의 whole-body decode와 streaming compression 한계를 exact package version evidence로 확인한다. proxy cap이나 custom bounded transport가 없으면 production raw-byte ceiling을 완료로 표시하지 않는다.
- interceptor의 resolved onion order, auth 이후 final invariant, total deadline, exactly-one retry owner와 cancel handle을 manifest에 고정한다.
@connectrpc/connect-query를 기본 도입하지 않는다. generated service/message는 adapter-private이고 mapped application value만 기존 TanStack Query에 들어간다.
10.3 Protobuf contract와 REST Gateway
Protobuf/REST Gateway 상세 결정은 VD-30이 소유한다.
- authenticated proto/Buf source, descriptor, generator/runtime/plugin version과 generated digest를 coherent release artifact로 고정한다.
- JSON 노출은 최소
WIRE_JSONcompatibility를 요구하고 canonical HttpRule route manifest와 generated OpenAPI를 별도 semantic diff한다. - current reference REST envelope에는 curated BFF를 유지한다.
- direct grpc-gateway/Envoy transcoder는 ProtoJSON, method/path/query/body, status/error/CORS/cache contract가 그대로 제품 API로 승인된 unary operation에만 적용한다.
- gateway는 idempotency store, CursorPage snapshot, ETag/304/412, file Range나 안전한 domain error vocabulary를 자동 제공하지 않는다.
- REST server streaming은 generated gateway의 부수 동작으로 활성화하지 않고
framing/terminal/cache를 소유하는 별도 ADR 없이는
NOT_SELECTED다.
11. Schema trust boundary
TypeScript type과 generated code는 compile-time convenience이지 runtime proof가 아니다. trust transition은 다음 순서를 지킨다.
untrusted bytes/frames
-> bounded transport decoder
-> transport envelope/status proof
-> operation DTO/message runtime or semantic proof
-> ValidatedWireValue (adapter-private)
-> boundary mapper
-> immutable application projection
schema profile은 최소 다음을 고정한다. browser가 직접 집행하는 decoded ceiling과 provider/BFF/proxy가 집행하는 wire/encoded ceiling의 owner를 분리한다.
schemaId
schemaVersion
boundary
protocol
sourceArtifactId + sourceDigest
unknownFieldPolicy
providerMaxEncodedBytes
maxDecodedBytes
maxDepth
maxNodes
maxStringBytes
maxCollectionItems
compatibilityPolicy
owner
unknown-field 기본 정책:
| 경계 | 정책 |
|---|---|
| request, config, capability, control envelope | REJECT_UNKNOWN |
| evolvable ordinary response DTO | STRIP_UNKNOWN 후 mapper에 전달 |
| discriminant/security/authorization 의미를 가진 union | unknown variant 거절 |
| unknown data 보존 | adapter 내부 forward proxy가 아닌 한 금지 |
현재 reference request/path DTO는 .strict()로 unknown field를 거절하고,
ordinary response DTO는 .strip() projection으로 additive server field를
cache/domain 경계 밖에 버린다. discriminant/security union을 포함한 다른
operation은 각 compatibility profile에 따라 별도로 결정한다.
12. Mapper 경계
mapper는 transport가 아니라 feature contract가 소유한다.
MapperDefinition
mapperId
inputSchemaId
outputContractId
mapperVersion
collectionPolicy
temporalPolicy
numericPolicy
nullabilityPolicy
owner
mapper는 pure, deterministic, side-effect-free이며 다음 union을 반환한다.
MappingResult<T>
= { ok: true, value: T }
| { ok: false, error: MAPPING_CONTRACT_VIOLATION }
현재 reference mapper는 예상 가능한 drift를 throw하지 않고 closed
MappingResult failure로 반환한다. registry 실행 경계는 예상하지 못한 mapper
throw도 fail-closed mapping failure로 바꾸며 raw DTO, value, path와 backend
message를 버린다.
공통 scalar 규칙:
- opaque ID는 trim/재해석하지 않는 bounded branded string이다.
- ISO timestamp는 offset/precision 정책을 검증한 뒤 application instant로 변환한다. locale date string과 invalid date normalization을 금지한다.
int64, decimal money와 high-precision value는 JSON number로 받지 않는다.null, absent와 empty string/list는 schema와 domain에서 별도 의미로 결정한다.- unknown enum은 domain이 explicit
UNKNOWN을 소유한 경우만 mapping한다. - collection mapping은 count/byte ceiling 안에서 fail-fast하고 partial array를 cache하지 않는다.
- mapper는 network, clock, storage, QueryClient, locale formatter와 telemetry를 호출하지 않는다.
13. Server State Cache 경계
TanStack Query는 transport response cache가 아니라 mapped application projection의 memory lifecycle owner다.
cache에 허용:
- immutable plain application projection
- registered query identity로 찾을 수 있는 bounded collection/page
- UI가 stale/refresh 상태를 계산하는 library metadata
cache에 금지:
Response, raw JSON/GraphQL envelope, generated protobuf message- auth/CSRF/idempotency token, request header와 arbitrary URL
- raw ETag, trace/span, backend error/details
- File/Blob/stream/native handle
- domain service, class instance, function, Promise와
AbortSignal
query profile은 caller가 raw option을 전달하는 대신 registry에서 선택한다.
ServerStateProfileV1
profileId
queryKeyCodecId
scopePersistencePolicyId
staleTimeMs
gcTimeMs
refetchOnFocus
refetchOnReconnect
networkMode
maxResultBytes
maxCollectionItems
paginationProfileId | null
revalidationProfileId | null
invalidationTopicRefs[] = { topicId, topicVersion }
placeholderPolicy
owner
query key는 validated/canonical application input과 scope projection으로 만든다. REST URL, GraphQL document/hash, protobuf bytes와 generated message serialization은 query key가 아니다. transport 교체가 use-case identity를 바꾸지 않으면 같은 application query family를 유지할 수 있지만, old/new representation을 한 cache entry에 shadow write하지 않는다.
network retry는 transport adapter가 소유하고 Query retry는 기본 false다.
refresh failure에서 유효한 previous data는 stale-degraded로 유지한다. schema,
mapper, scope, authorization와 contract mismatch는 stale data를 계속 노출해도
되는지 query profile이 명시해야 하며 기본은 security-sensitive scope에서
즉시 숨김/clear다.
VD-13이 scope/persistence profile, normative key layout, account/session generation과 late-result fence를 소유한다. 이 문서는 그 profile을 exact join하고 operation/cache policy, pagination, revalidation과 mutation coherence를 소유한다.
14. Pagination과 conditional revalidation
cursor pagination은 다음 binding을 갖는다.
PaginationBinding
query family fingerprint
canonical filters/sort
scope fingerprint
server snapshot/revision policy
page size ceiling
opaque next cursor
- cursor를 decode하거나 URL로 취급하지 않는다.
hasMore === (nextCursor !== null)을 codec에서 강제한다.- single-page cache는 runtime-scoped cursor fingerprint를 semantic key에 포함하고,
infinite query만 cursor를 root key에서 제외해 bounded
pageParam으로 둔다. - max pages/items/estimated bytes를 넘으면 더 불러오지 않는다.
- 동일 cursor 반복, loop와 non-progress page를 contract failure로 닫는다.
- offset pagination의 insert/delete drift를 자동 deduplicate로 숨기지 않는다.
- page merge는 mapper가 보장한 stable identity가 있을 때만 deterministic하다.
- previous filters의 page를 새 filter key에 재사용하지 않는다.
REST 304는 cached data가 있다는 뜻이 아니라 representation이 바뀌지 않았다는
transport 결과다. exact query fingerprint, scope/generation과 cached mapped
value에 binding된 validator record가 모두 있을 때만 freshness를 갱신한다.
cached value가 없거나 binding이 다르면 unconditional request를 한 번 수행하거나
closed failure로 끝낸다.
GraphQL persisted operation과 gRPC-Web unary는 기본적으로 application-level validator가 없다. backend가 revision을 제공하면 response schema/mapper가 opaque revision을 application revalidation policy로 투영해야 하며 HTTP/gRPC metadata를 임의로 ETag처럼 해석하지 않는다.
15. Mutation coherence
mutation과 query cache는 server commit authority가 아니다.
validate command
-> derive logical key + exact command equality/opaque identity token
-> runtime/scope coordinator applies concurrency + duplicate admission
-> separately acquire invalidation-topic hint-coalescing lease
-> cancel exact affected query reads
-> capture bounded base cache revision and inverse patch
-> install own ordered optimistic layer with revision CAS
-> transport derives backend idempotency binding from operation policy
-> execute command once with transport-owned retry policy
-> success: registered exact seed/compare-and-apply + list/aggregate invalidation
-> rejection: remove or invert only own layer with revision CAS
-> uncertainty/CAS miss: preserve other commits + invalidate/authoritative refetch
-> release invalidation lease + concurrency admission
- duplicate submit 정책은
JOIN_IDENTICAL,REJECT_DUPLICATE,ALLOW_INDEPENDENT중 operation별로 고정하고, 동일성은 전체 validated semantic input의 runtime-private exact equality guard와 opaque identity token으로 판정한다. - 같은 hook instance의 Promise dedupe를 server idempotency로 간주하지 않는다.
- logical key/identity admission은 local ordering이고 invalidation-topic lease는 remote hint coalescing일 뿐이다. 둘 다 backend idempotency authority가 아니다.
- optimistic patch는 raw DTO나 generated message를 만들지 않는다.
- snapshot item/byte ceiling을 넘으면 optimistic update를 하지 않고 pending UX만 제공한다.
- 409/GraphQL conflict code/gRPC
ABORTED는 동일한 safe conflict vocabulary로 mapping하되 server revision과 merge policy는 feature use case가 소유한다. - server가 commit한 뒤 local invalidation 실패를 command 실패로 되돌리지 않는다. cache health를 degraded로 기록하고 bounded refetch/recovery를 예약한다.
- command의 자동 protocol failover는 중복 side effect 위험 때문에 금지한다.
16. Server streaming과 cache
gRPC-Web server stream, GraphQL subscription과 incremental delivery는 ordinary queryFn과 다르다.
현재 installed API operation registry는 terminal REST operation만 소유한다.
GraphQL HTTP와 gRPC-Web unary/server-stream은 각 reference adapter가 구현될 때
protocol discriminant와 전용 binding으로 확장한다. SSE, WebSocket과 Web Push는
VD-28 realtime registry가 소유하며 bounded polling은 registered terminal REST
QUERY의 scheduling policy이지 새 protocol이나 automatic failover가 아니다.
GraphQL @defer/@stream은 선택될 경우에도 한 HTTP operation의 finite
incremental response이며 subscription과 같은 장기 realtime stream이 아니다.
ServerStreamPort
open(frozen request, signal)
-> AsyncIterable<Result<MappedEvent>>
-> close()
- frame/event마다 schema, mapper, scope와 generation을 재검증한다.
- sequence, duplicate, gap과 resume token은 backend application protocol이다.
- bounded queue, high-water mark, overflow, idle/total deadline을 선언한다.
- stream event는 registered reducer로 immutable snapshot을 만들거나 query invalidation hint만 발행한다.
- partial event를 ordinary query success로 cache하지 않는다.
- stream 종료/재connect를 TanStack Query retry로 처리하지 않는다.
GraphQL subscription은 현재 NOT_SELECTED이고, gRPC-Web server-stream은
DESIGNED_NOT_IMPLEMENTED다.
gRPC-Web ServerStreamPort는 operation-bound outbound stream result일 수 있다.
API adapter가 protobuf frame/message decode, semantic schema와 boundary mapper를
끝낸 뒤 제품이 runtime-wide notification projection을 명시적으로 선택한
branch에서만 mapped event를 VD-28 common coordinator/FeatureEventInput에
전달한다. protobuf를 REALTIME_EVENT_V1 JSON으로 감싸지 않고, 첫 event 전
opening replay와 이후 resume 규칙은 VD-27이 계속 소유한다.
17. Backend/provider 계약
구현 owner, 권장 topology, 현재 reference endpoint/envelope, idempotency store, Cursor/ETag/revision과 protocol별 handoff checklist는 Backend API와 Server State contract가 소유한다. 아래 표는 frontend 설계가 요구하는 경계 요약이다.
| 경계 | backend/provider가 제공할 계약 |
|---|---|
| 공통 | authorization, contract version/artifact compatibility, encoded transfer와 decoded payload의 bounded owner, stable error code, correlation/trace projection, idempotency와 rate-limit semantics |
| REST | exact method/path/media/status/envelope, CSRF strategy, idempotency retention, cursor binding, ETag/If-None-Match 또는 revision, retry-safe status와 CORS/cache policy |
| GraphQL | schema registry, persisted-operation manifest, allowlist/cost/depth enforcement, safe error extension vocabulary, operation retirement과 N/N-1 router rollout |
| Connect | proto/descriptor/codegen source, exact Connect-Web runtime/encoding/method, EndStream/status/error, timeout/cancel/compression/CORS와 browser/server conformance |
| gRPC-Web | proto/descriptor source, Buf/protoc compatibility policy, gRPC-Web proxy, exact service/method, message/frame ceiling, status/trailer/CORS exposure와 stream resume protocol |
| Protobuf REST Gateway | selected gateway kind/version, HttpRule/ProtoJSON/OpenAPI artifact, path/query/body/status/error/header mapping, edge→upstream cancellation과 N/N-1 conformance |
| Schema | authenticated source artifact, additive/breaking classification, deprecation window, fixtures and source digest |
| Mapper | domain meaning, temporal/numeric/null/enum semantics와 stable identity |
| Cache | revision/conflict/idempotency/invalidation semantics; frontend TTL은 authorization 대체가 아님 |
frontend가 제공하는 Zod schema, generated type과 cache invalidation은 backend authorization, validation, idempotency와 conflict resolution을 대체하지 않는다.
18. Security와 privacy
- API base/GraphQL/Connect/gRPC-Web endpoint는 HTTPS registry ID로 고정한다.
- caller가 URL, header, GraphQL document, service/method와 metadata를 제출하지 못한다.
- auth owner가 credential을 붙이고 application/query/cache에는 token을 노출하지 않는다.
- credential attachment 뒤 URL/method/origin/body digest와 registry-owned header binding을 재검증한다. auth integration unavailable 상태에서 request를 보내지 않는다.
- cookie session의 unsafe request는 CSRF token/header 또는 same-site BFF 정책을 operation profile과 provider conformance로 증명한다.
- GET/GraphQL variables에 sensitive filter를 넣는 operation은 별도 review 없이 만들지 않는다.
- response byte/depth/node/string/collection/frame cap으로 resource exhaustion을 막는다.
- GraphQL cost/depth와 gRPC message cap은 server/proxy에서도 강제한다.
- mapper와 cache는 prototype/accessor/class/native object를 받아들이지 않는다.
- PII/business ID를 query key, diagnostics label, persisted cache physical key에 직접 넣지 않는다. 필요한 identity는 opaque partition/token policy를 쓴다.
- command identity token/logical mutation key는 runtime-local control data이며 diagnostics, cross-context wire와 persistence에 넣지 않는다.
- raw request/response, GraphQL variables/errors, protobuf bytes/metadata, ETag, cursor, idempotency key와 validation value를 관측 데이터에 넣지 않는다.
19. Observability
허용된 bounded aggregate:
- operation registry ID, protocol, semantics
- outcome/error kind, HTTP status group 또는 gRPC status code allowlist
- GraphQL full/partial/rejected outcome
- attempt/deadline/duration/encoded-byte/result-item/frame bucket
- schema/mapper profile ID와 compatibility outcome
- query hit/miss/stale/refetch/eviction bucket
- mutation optimistic/rollback/conflict/invalidation outcome
- provider/browser/runtime version의 low-cardinality bucket
금지:
- URL/path parameter/search/body/header
- GraphQL document, variables, response path와 raw error message
- protobuf message/metadata/trailer raw value
- resource/account/tenant ID, cursor, validator, digest와 cache value
하나의 logical operation은 terminal observation 하나를 만든다. attempt span은 sampling된 내부 detail로만 남기며 terminal success/failure count를 중복시키지 않는다.
20. Failure와 fallback
| 실패 | 기본 결과 |
|---|---|
| registry/schema/mapper 누락 | network 전 fail-closed, operation traffic disable |
| incompatible contract artifact | product mount 또는 해당 capability admission 차단 |
| response cap/decode/schema mismatch | body/reader cancel, cache write 금지, provider incompatibility |
| mapper violation | cache write 금지, safe contract failure |
| REST retry exhaustion | stale 허용 profile만 previous data 유지 |
| GraphQL persisted operation missing | full document fallback 금지, coherent artifact rollback |
| GraphQL partial data | default reject; explicit profile만 completeness와 함께 사용 |
| Connect missing/duplicate EndStream 또는 oversize whole body | call cancel, cache write 금지, provider/runtime incompatible |
| gRPC-Web proxy/trailer mismatch | reader cancel, provider unavailable/incompatible |
| REST Gateway HttpRule/OpenAPI/runtime drift | affected route admission 차단, coherent gateway artifact rollback |
| server stream gap/overflow | snapshot 폐기 또는 authoritative refetch |
| account/generation mismatch | late result 폐기, old-scope cache write 금지 |
| invalidation failure after commit | command 성공 유지, cache degraded + recovery refetch |
GraphQL, Connect 또는 gRPC-Web failure를 REST로 자동 전환하지 않는다. 사전에 등록된 read-only shadow/fallback operation이 있고 동일 authorization/mapper/result contract를 conformance suite로 증명한 경우만 selector가 새 logical query를 시작할 수 있다.
21. Rollout과 removal
ADR + registry schema accepted
-> deterministic codec/schema/mapper fixture
-> provider-neutral adapter and fake
-> negative boundary/removal gate
-> AVAILABLE_NOT_COMPOSED
-> product/provider/operation selection
-> bootstrap composition behind TrafficAdmission=DISABLED
-> COMPOSED
-> shadow/read-only conformance
-> browser/provider/operations evidence
-> PromotionEvidence=COMPLETE
-> CANARY
-> ENABLED
REST v2 local baseline은 installed reference operation에 연결됐다. 실제 provider traffic은 operation/profile 단위의 conformance와 canary를 거쳐야 하며, conditional/pagination은 backend 계약 없이 enabled하지 않는다. GraphQL/Connect/gRPC-Web/codegen/gateway dependency는 실제 selected operation이 없으면 production inventory에 없어야 한다.
removal:
- 신규 operation admission을 닫는다.
- query는 cancel하고 command/stream은 bounded drain 또는 explicit abort한다.
- 해당 invalidation listener, auth attachment와 provider를 close한다.
- current scope의 mapped memory cache를 clear한다.
- operation/schema/mapper/query profile과 generated artifact를 제거한다.
- dependency, config, proxy route, test fixture와 production module inventory가 함께 제거됐음을 증명한다.
- backend persisted-operation/method retirement은 N/N-1 client window 뒤에 한다.
22. Test와 promotion evidence
Deterministic
- operation registry closed union/reference/orphan/duplicate
- request canonicalization과 query-key identity
- timeout/total deadline/retry/idempotency/auth recovery
- response byte/depth/node/collection cap
- schema unknown-field, scalar, null/enum/numeric/date matrix
- mapper success/failure/no raw value leakage
- query stale/gc/refetch/pagination/mutation/rollback/generation fence
Contract
- REST OpenAPI/envelope/status/media/cursor/conditional/idempotency
- GraphQL schema + persisted manifest + variables/result/error/partial policy
- proto descriptor + breaking check + gRPC status/trailer/frame fixture
- Connect unary/stream JSON/binary/GET/EndStream/CORS/deadline fixture
- HttpRule route manifest + ProtoJSON/OpenAPI/status/error/header mapping fixture
- 같은 fixture를 fake, emulator/staging과 actual provider에 실행
Browser/integration
- bootstrap → feature → transport → schema → mapper → Query → UI
- AbortSignal/navigation/logout/account switch
- CORS/cookie/CSRF/redirect/content-encoding
- HTTP/2/proxy/CDN/Connect EndStream/gRPC-Web trailer behavior
- offline/reconnect/focus와 stale-degraded UI
Fault
- truncated/oversize/malformed response
- slow credential/network/body/schema/mapper phase
- 401 recovery, 429, retry exhaustion과 total deadline
- GraphQL partial/error/persisted-operation drift
- Connect missing/early/duplicate EndStream, whole-body overflow와 compression mismatch
- gRPC missing/conflicting terminal status source, corrupt/compressed/oversize frame와 stream gap
- REST Gateway route/OpenAPI/runtime rewrite drift와 abort propagation loss
- late response, duplicate mutation, optimistic rollback과 invalidation failure
Operations
- operation kill switch
- contract artifact N/N-1 rollout과 rollback
- provider incompatibility containment
- cache scope reset와 stale-data decision
- generated client/GraphQL/Connect/gRPC-Web/REST Gateway removal drill
fake와 generated compile success만으로 actual provider, browser나 operations
evidence를 COMPLETE로 표시하지 않는다.
23. 설계 우선 work package
| package | 목표 |
|---|---|
| API-01 | REST v2 operation registry, total deadline, bounded decoder와 conditional/pagination contract |
| API-02 | multi-protocol schema artifact/digest governance와 typed Mapper result |
| API-03 | strict ServerStateProfile, query-key codec, pagination/revalidation와 mutation policy |
| API-04 | persisted-operation-only GraphQL reference adapter와 conformance harness |
| API-05 | gRPC-Web unary/server-stream reference adapter와 proxy harness |
| API-06 | Connect-Web unary/server-stream reference adapter와 provider harness |
| API-07 | Protobuf governance와 selected REST Gateway conformance |
| API-08 | atomic composition, readiness, kill switch, runbook와 provider/browser evidence |
권장 순서는 API-01 → API-02 → API-03이다. API-04~07은 제품 선택과 backend/provider 계약이 생긴 branch만 독립적으로 시작한다. GraphQL, Connect, gRPC-Web과 REST Gateway를 “미래 대비” 목적으로 모두 기본 bundle에 설치하지 않는다.
24. 완료 기준
- 모든 installed operation은 protocol/schema/mapper/cache/error/deadline owner가 있다.
- application/presentation public type에 DTO, GraphQL SDK와 generated protobuf가 없다.
- untrusted byte부터 mapped projection까지 모든 ceiling과 trust transition이 닫혀 있다.
- query key와 실제 request input이 동일 canonical source에서 파생된다.
- transport retry와 Query retry가 중복되지 않는다.
- command idempotency, optimistic patch와 conflict/invalidation owner가 명시돼 있다.
- account/logout/release generation 뒤 late result가 cache에 들어가지 않는다.
- actual REST/GraphQL/Connect/gRPC-Web/Gateway provider에 같은 semantic conformance fixture를 실행한다.
- contract drift, kill switch, rollback과 optional dependency removal drill이 통과한다.
- raw payload/URL/document/message/metadata/validator가 log와 cache에 없다.
COMPOSED와 production-ready/provider-conformant를 같은 의미로 쓰지 않는다.