Files
clean-architecture-frontend…/docs/architecture/decisions/VD-23-api-transport-selection-and-rest-execution.md
T
DongHyeonkaandClaude Opus 5 e06e4377ca fix: preserve command effect certainty across retries
Separate per-attempt physical state from the logical execution history. The
executor now keeps one monotonic certainty accumulator joined through
joinMutationEffectCertainty, records MAYBE_APPLIED at dispatch, and reads the
accumulator from every retry-loop fence, final-invariant, cancellation and
timeout return.

A retry-time scope fence landing between the loop-entry check and the
pre-dispatch invariant can no longer downgrade an already dispatched command to
NOT_STARTED.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:01:48 +09:00

28 KiB

VD-23: API transport selection과 REST execution

  • 상태: Accepted — REST v2 security/execution baseline composed, advanced profiles pending
  • 결정일: 2026-07-28
  • installed REST reference vertical: COMPOSED
  • REST v2 security/execution baseline: COMPOSED
  • provider/path/auth profile baseline: COMPOSED
  • conditional execution/provider-conformance delta: DESIGNED_NOT_IMPLEMENTED
  • GraphQL/Connect/gRPC-Web/REST Gateway product selection: NOT_SELECTED
  • 관련 결정: VD-13, VD-24, VD-25, VD-26, VD-27, VD-29, VD-30
  • 상세 설계: API contract, Schema, Mapper와 Server State
  • browser Protobuf/gateway 설계: Protobuf browser transport와 REST Gateway

배경

현재 reference feature는 operation registry, request Zod schema, shared HTTP executor, response envelope/payload schema, mapper, gateway, application input과 TanStack Query까지 실제 production composition에 연결된다. 따라서 REST 경로 자체를 미구현으로 표시하지 않는다.

현재 reference operation은 v2 metadata, collision-aware composition, allowlisted credential patch, auth fail-before-fetch, prefix-preserving target, shared logical deadline, exact JSON media/success status, bounded decoder와 typed mapping failure를 실행한다. correlation header, success status group과 401 replay를 포함한 physical attempt도 terminal observation에 연결한다.

operation은 path placeholder↔codec key exact join, named provider/bearer/CSRF profile, provider credential-mode ceiling과 encoded query byte ceiling도 실행한다. 남은 production delta는 cookie-CSRF/CORS evidence, 204/304/412 conditional transaction, artifact digest와 실제 provider conformance다. API_CONTRACT_VERSION 문자열 일치만 backend compatibility 증거로 사용하지 않는다.

결정

1. Protocol 선택은 operation registry가 소유한다

feature application port는 protocol-neutral이다.

feature use case
  -> feature gateway
       -> exact semantic operation
            -> REST adapter
            -> persisted GraphQL adapter
            -> Connect adapter
            -> gRPC-Web adapter
  • operationId는 한 protocol에만 binding한다.
  • UI, query hook과 use case는 protocol을 선택하지 않는다.
  • URL, GraphQL document, service/method와 generated request를 application input으로 받지 않는다.
  • 같은 command를 provider failure 때문에 다른 protocol로 자동 replay하지 않는다.
  • read fallback도 동일 auth/freshness/schema/mapper/query identity와 하나의 total retry budget을 증명한 registered policy만 허용한다.
  • GraphQL/Connect/gRPC-Web adapter를 공통 HttpClient의 mode flag로 넣지 않는다. 공통으로 공유하는 것은 execution context, auth collaboration, failure vocabulary와 observation뿐이다.

2. REST operation은 실행 source와 정적 manifest를 분리하지 않는다

목표 API는 feature가 generic이 연결된 definition을 만든다.

defineRestOperation<
  Path,
  Search,
  Body,
  SuccessWire,
  SuccessValue,
  Failure
>({
  common,
  providerId,
  method,
  uriTemplate,
  pathCodec,
  searchCodec,
  bodyCodec,
  successCodec,
  errorCodec,
  mapper,
  policies
})

실행 definition에서 registry manifest를 결정적으로 투영한다. schema metadata, 실제 codec map, operation-specific TypeScript map과 mapper를 서로 다른 string dispatch table에 수기로 중복하지 않는다.

manifest 최소 field:

protocol = REST
registryVersion
providerId
operationId
owner
semantics
method
relativePathTemplate
pathSchemaId
searchSchemaId
bodySchemaId
requestMediaProfile
successStatusProfiles[]
errorStatusProfiles[]
responseSchemaId
mapperId
authProfileId
csrfProfileId
replayPolicy
idempotencyKeyPolicy
deadlineProfileId
retryProfileId
paginationProfileId | null
conditionalProfileId
serverStateProfileId | null
maxRequestBytes
maxDecodedResponseBytes
maxResponseItems
observabilityProfileId
compatibility

3. Contribution composer는 collision 전에 실패한다

feature contributions[]
  -> preserve every source row
  -> validate contribution owner/version
  -> detect duplicate operation/schema/mapper/query/topic ID
  -> resolve every codec/mapper/profile reference
  -> validate semantic coherence
  -> freeze installed registry
  -> emit compatibility manifest

object spread의 last-write-wins를 금지한다. duplicate ID가 payload까지 동일해도 owner를 하나 선택하지 않고 build를 실패시킨다. alias/rename은 versioned migration row로만 허용한다.

semantic coherence:

  • GET/HEAD는 body 없음, SAFE만 허용
  • HEAD는 body success codec 없음
  • POST/PATCH retry는 IDEMPOTENT | KEYED_COMMAND 또는 명시적 SAFE semantics 필요
  • KEYED_COMMANDidempotencyKeyPolicy=REQUIRED, 다른 replay policy의 key attach/금지는 exact key profile과 일치
  • NON_REPLAYABLE은 retry와 401 replay 금지
  • success/error status가 겹치지 않음
  • 204 profile은 body/schema 없음
  • 304는 query + conditional profile + existing cache binding 필요
  • 412는 precondition profile 필요
  • path placeholder 집합과 path codec key 집합이 exact match
  • query/cache profile은 QUERY operation에만 연결
  • mutation invalidation은 registered topic만 사용

4. Provider endpoint와 URI

application은 provider URL을 받지 않는다.

RestProviderProfile
  providerId
  baseOrigin
  basePathPrefix
  allowedCredentialsModes
  corsProfile
  referrerPolicy
  redirectPolicy = ERROR
  defaultHeaders
  allowedResponseOrigins
  • non-local은 HTTPS만 허용한다.
  • provider는 credential mode ceiling만 제공하고 operation의 auth transport profile이 exact mode를 선택한다. anonymous는 omit, same-origin cookie는 approved same-origin, approved cross-origin cookie만 include다.
  • base URL의 username, password, query와 fragment를 금지한다.
  • exact origin과 canonical base path prefix를 보존한다.
  • operation template은 relative API path이며 scheme, authority, query와 fragment를 포함하지 않는다.
  • leading slash가 base prefix를 제거하는 new URL() ambiguity를 쓰지 않는다.
  • encoded slash, dot segment, NUL/control, duplicate slash와 overlong path를 정책대로 거절한다.
  • redirect는 기본 error다. 로그인/다운로드 handoff는 일반 JSON REST operation과 다른 capability다.

path parameter:

  • path codec의 parsed output만 encode한다.
  • missing, extra, empty와 length 초과 parameter를 network 전에 거절한다.
  • Unicode normalization을 업무 ID에 임의 적용하지 않는다.
  • path value와 최종 URL은 diagnostics에 기록하지 않는다.

query:

  • key ordering, repeated-array/comma style, boolean, null/absent/empty semantics를 profile에 고정한다.
  • URL encoded byte ceiling을 적용한다.
  • raw URLSearchParams, query string과 next URL을 caller에게 받지 않는다.
  • sensitive/private value를 GET query에 넣는 operation은 별도 security review가 없으면 금지한다.

5. Request projection과 final invariant

request는 다음 소유 순서로 만든다.

operation + validated input
  -> immutable request binding
  -> body canonical serialization + digest
  -> transport-owned headers/options
  -> constrained auth/CSRF patch
  -> final invariant validation
  -> fetch

transport-owned header:

  • Accept, Content-Type
  • contract/media version
  • bounded correlation/trace context
  • idempotency key
  • conditional validator
  • approved CSRF header

caller와 feature mapper가 arbitrary header를 추가하지 않는다.

auth owner target:

CredentialPatch
  credentialMode
  allowlisted header name/value
  proof expiry/generation

operation auth profile은 final Fetch credentials를 exact하게 고정한다. ANONYMOUS | BEARER_HEADER는 ambient cookie가 섞이지 않게 omit, same-origin cookie session은 same-origin, cross-origin cookie는 별도 CORS/CSRF provider evidence가 있는 profile만 include다. provider ceiling과 맞지 않으면 fetch 0회다.

auth owner가 Request 전체를 반환하지 않는다. transition 기간에 current port를 사용한다면 attach 전후의 다음 값이 exact하게 같아야 한다.

  • URL/origin/path/query
  • method
  • body digest
  • content type/length
  • correlation, idempotency, conditional와 CSRF binding
  • redirect/cache/referrer/credentials/mode

다르면 AUTH_INTEGRATION_FAILURE, fetch 0회다.

auth-required operation은 session state가 authenticated가 아니면 fetch하지 않는다. integration-failed, unauthenticated와 credential attach rejection을 anonymous request로 downgrade하지 않는다.

5-0. Logical effect certainty는 단조 증가한다

PhysicalAttemptState는 현재 attempt만 설명한다. logical execution 전체에는 별도의 monotonic accumulator를 두고 joinMutationEffectCertainty로 join한다. join 순서는 보수적이다.

NOT_STARTED < NOT_APPLIED < MAYBE_APPLIED < APPLIED_CONFIRMED

fetch() dispatch 시점에 command는 즉시 MAYBE_APPLIED를 기록한다. 이후 retry loop entry, pre-dispatch final invariant, scope fence, cancellation, timeout return은 모두 accumulator를 읽는다. 아직 보내지 않은 새 retry가 있다는 이유로 전체 logical operation을 NOT_STARTED로 되돌리지 않는다. query operation은 NOT_APPLICABLE로 남고 이 lattice를 쓰지 않는다.

5-1. Installed auth profile registry (V3 집행)

installRestAuthProfileRegistry()가 composition 시점에 profile을 한 번 설치하고 INSTALLED_REST_AUTH_PROFILES가 유일한 authority다. contract composition (assertExecutionPolicy)은 등록되지 않은 authProfileId를 거절하므로 executor는 runtime에 profile을 발명하지 않는다. profile은 다음을 exact하게 소유한다.

  • Fetch credentials (credential collaborator가 바꿀 수 없다)
  • allowedCredentialHeaders: 이 operation이 허용하는 정확한 proof header 집합
  • requiredCredentialHeaders: dispatch 전에 반드시 관찰되어야 하는 집합

CredentialPatchOutcome.READY는 proof header만 담는다. credentials field는 제거되었다. credential owner가 transport-owned header(accept, content-type, idempotency-key)나 forbidden header를 넣거나, profile이 허용하지 않는 header를 넣거나, required header를 빠뜨리면 AUTH_INTEGRATION_FAILURE이고 fetch 0회이며 command effect는 NOT_STARTED다. idempotency-key는 contract-owned이므로 더 구체적인 UNEXPECTED_IDEMPOTENCY_KEY request violation으로 남는다. UNAUTHENTICATED는 user/session state이지 integration failure가 아니다.

transport-owned header는 credential header 뒤에 기록되어 key ordering으로도 shadow될 수 없고, final invariant가 init.credentials와 profile을 다시 대조하며 allowed/required credential header 집합을 독립적으로 재검증한다.

AUTH_MODE=demo는 profile을 약화시키지 않는다. createDemoSessionAdapter가 고정된 비밀 아닌 DEMO_AUTHORIZATION_MARKER proof header를 제공하여 strict REFERENCE_EXTERNAL_BEARER를 그대로 만족시킨다. 진짜 anonymous backend는 별도 anonymous contract/profile을 composition에서 선택해야 한다.

credential collaborator는 AuthOperationContext { signal, deadlineAtMonotonicMs }를 받는다. cooperative owner는 스스로 중단하고, non-cooperative owner도 executor가 같은 lifetime signal과 race하므로 operation 수명을 넘기지 못하며 late completion은 관찰되지 않는다.

same-origin BFF cookie session을 기본 권장한다.

  • cookie는 Secure/HttpOnly이며 provider가 SameSite 정책을 소유한다.
  • unsafe method는 server의 exact Origin/Fetch Metadata 검증과 composition-issued anti-CSRF proof를 요구한다.
  • CSRF proof는 application/query/cache에 노출하거나 persistence하지 않는다.
  • custom content type/preflight가 있다는 사실만 CSRF 방어로 간주하지 않는다.

cross-origin profile은 다음을 actual provider에서 증명한다.

  • exact Access-Control-Allow-Origin, wildcard 금지
  • credentials mode와 allow-credentials 일치
  • exact allow-method/allow-header
  • 필요한 request ID, ETag, Retry-After만 expose
  • OPTIONS와 actual response의 policy 동등성
  • redirect 없음

7. Replay와 idempotency

ReplayPolicy
  SAFE
  IDEMPOTENT
  KEYED_COMMAND
  NON_REPLAYABLE
  • SAFE: read-only이며 network/recovery retry 가능
  • IDEMPOTENT: 같은 principal/operation/payload/precondition의 반복 request가 의도한 server effect를 추가로 만들지 않으며 duplicate response/status mapping을 backend contract가 명시한다. response byte가 항상 동일하다는 뜻은 아니다.
  • KEYED_COMMAND: application logical command lease가 key를 생성하고 lifecycle 전체에서 유지
  • NON_REPLAYABLE: ambiguous result에서 자동 재실행 금지

KEYED_COMMAND binding:

principal scope
operation ID/version
canonical payload digest
idempotency key
server retention/expiry

backend는 atomic claim, concurrent same-key join/replay, same-key different-payload rejection과 terminal receipt를 제공한다. header를 보냈다는 사실만 replay safety가 아니다. provider conformance가 없으면 retry/401 recovery traffic을 켜지 않는다.

current client가 execute 호출마다 key를 생성하는 방식은 network attempt 안에서는 재사용되지만 ambiguous terminal 뒤 사용자 retry와 연결되지 않는다. 목표 command owner가 effect certainty를 다음처럼 반환한다.

NOT_APPLIED | COMMITTED | UNKNOWN

UNKNOWN은 새 key 자동 retry가 아니라 status/reconcile 또는 명시적 사용자 복구로 닫는다.

8. Logical deadline, cancel과 retry

LogicalExecutionBudget
  totalDeadlineMs
  attemptTimeoutMs
  maxAttempts
  maxCumulativeSleepMs
  maxRetryAfterMs
  authRecoveryCount = 0 | 1

total deadline은 다음 모두를 포함한다.

  • operation/schema lookup과 request encode
  • credential/CSRF attachment
  • fetch attempt
  • body read/decode/schema/mapper
  • 401 recovery
  • retry backoff/Retry-After

각 phase는 남은 total budget보다 긴 timer를 만들지 않는다. timer, abort listener, response reader와 auth waiter는 모든 terminal path에서 정리한다.

초회, network/status retry와 401 recovery replay를 포함한 모든 API provider fetch는 하나의 monotonic physicalAttemptCount를 증가시키고 maxAttempts를 소비한다. authRecoveryCount는 추가 상한일 뿐 attempt counter, sleep budget이나 total deadline을 reset하거나 우회하지 않는다.

retry status는 operation의 exact subset만 허용한다.

network failure
408
429
502
503
504
  • 429와 503의 Retry-After를 injected clock으로 parse한다.
  • hard ceiling을 넘는 Retry-After는 sleep하지 않고 terminal로 닫는다.
  • full jitter와 attempt/sleep/elapsed 세 상한을 모두 적용한다.
  • schema, mapper, 4xx validation/authz/conflict와 redirect failure는 retry하지 않는다.
  • caller cancel, navigation supersede, runtime teardown, attempt timeout과 logical deadline을 다른 safe failure로 유지한다.
  • 401 recovery는 auth-required이면서 replay-safe한 operation만 한 번 수행한다.
  • 동시 401은 session owner의 single-flight recovery를 공유한다.
  • physical attempt, logical retry와 auth replay를 따로 관측한다.

9. Closed execution

public executor는 promise rejection 대신 항상 closed result로 끝난다.

RestExecutionResult<T>
  SUCCESS
  VALIDATION_REJECTED
  AUTH_REQUIRED | AUTH_INTEGRATION_FAILURE
  REQUEST_ABORTED
  REQUEST_ATTEMPT_TIMEOUT | REQUEST_DEADLINE_EXCEEDED
  NETWORK_UNREACHABLE
  RATE_LIMITED | SERVER_FAILURE
  HTTP_FAILURE
  CONTENT_TYPE_MISMATCH | BODY_LIMIT_EXCEEDED | MALFORMED_BODY
  SCHEMA_MISMATCH | MAPPING_CONTRACT_VIOLATION
  PRECONDITION_FAILED | CONFLICT
  CONTRACT_INCOMPATIBLE

attempt timer와 전체 logical deadline은 error registry, safe user copy와 telemetry bucket에서도 별도 closed kind로 유지한다. 둘 다 raw URL/timing detail을 노출하지 않는다.

operation lookup, URL construction, Headers, body serialization, Request, auth collaboration, fetch, read, parse, schema와 mapper를 모두 catch/normalize 경계 안에 둔다. thrown value/body/header/URL을 failure에 복사하지 않는다.

10. Response admission과 bounded decoder

body를 Response.json()으로 바로 읽지 않는다.

response
  -> final URL/status/header admission
  -> exact media type parser
  -> present/valid Content-Length advisory preflight
  -> bounded stream reader
  -> actual browser-visible decoded byte count
  -> UTF-8/profile decoder
  -> JSON structural ceiling
  -> envelope/status codec
  -> operation response codec
  -> mapper

browser Fetch의 Response.body는 일반적으로 content decoding 뒤 stream이므로 client가 actual wire/encoded byte를 신뢰성 있게 세었다고 주장하지 않는다.

  • BFF/proxy/CDN가 encoded transfer와 decompression ratio ceiling을 집행한다.
  • browser client는 present/valid Content-Length를 advisory rejection에만 쓰고 actual decoded bytes를 hard cap으로 센다.
  • 표준 JSON.parse profile은 decoded-byte cap이 pre-parse resource guard이고 depth/node/key/string/item cap은 materialization 뒤 admission guard다.
  • 구조 cap을 parse 중 강제해야 하는 더 큰 profile은 bounded tokenizing JSON parser를 별도로 선택하고 actual browser evidence를 가져야 한다.

cap 초과, truncation과 invalid UTF-8에서 reader를 cancel하고 cache에 쓰지 않는다.

media parser는 type/subtype/parameter를 exact하게 해석한다.

  • application/json
  • approved vendor application/*+json
  • RFC Problem Details profile
  • explicit 204 no-content

includes("application/json") 검사는 목표 계약이 아니다.

각 success status는 response codec을 가진다. 현재 envelope는 REST_ENVELOPE_V1 profile로 유지할 수 있지만 모든 REST provider에 강제하지 않는다. success status와 error body가 모순되면 status/profile 계약 실패다.

11. Error projection

backend error는 먼저 status/media별 codec을 통과한다.

  • raw message, stack, body와 arbitrary extensions를 버린다.
  • code/category는 operation error profile의 allowlist로 mapping한다.
  • unknown backend code는 closed generic failure다.
  • validation issue는 최대 count, path/code byte/charset와 allowed path를 제한한다.
  • request ID, trace ID와 correlation ID도 length/charset cap을 적용한다.
  • retryable backend boolean을 client retry authority로 사용하지 않는다.
  • 401/403/404 existence-hiding은 provider/product policy를 따른다.
  • 409 business conflict와 412 representation precondition failure를 분리한다.

12. Cursor pagination

CursorPageWire<T>
  items
  nextCursor | null
  hasMore
  snapshotToken | null

response codec은 item count, item size, cursor/snapshot byte와 total decoded ceiling을 검증한다. mapper는 immutable CursorPage<ApplicationProjection>을 만든다.

  • cursor는 opaque이며 decode/로그/telemetry 금지
  • arbitrary next URL을 따라가지 않음
  • filters/sort/scope/snapshot과 cursor를 exact binding
  • same cursor 반복, hasMore=true인데 cursor 없음, non-progress loop 거절
  • maximum pages/items/cache bytes 이후 fetch 중지
  • TanStack infinite query의 root key에는 semantic filter만 넣고 cursor는 bounded pageParam으로 관리
  • offset pagination은 stable small dataset이 증명된 별도 profile만 허용

current reference list array는 complete pagination 구현이 아니다.

13. Conditional read와 optimistic concurrency

operation별 owner:

ConditionalProfile
  NO_STORE
  APP_ETAG
  BROWSER_HTTP_CACHE
  APPLICATION_REVISION

모든 operation은 한 profile을 가져야 한다.

  • NO_STORE, APP_ETAG, APPLICATION_REVISION은 Fetch cache=no-store.
  • BROWSER_HTTP_CACHE만 exact browser cache mode와 server Cache-Control/Vary contract를 사용한다.
  • caller/library default에 맡기는 implicit NONE은 없다.

APP_ETAG:

  • strong ETag/generation은 adapter-private metadata
  • exact operation/query/scope/representation binding과 함께 memory에 보존
  • If-None-Match를 transport가 생성
  • 304는 same binding의 mapped cached value가 있을 때만 freshness 갱신
  • 304는 same query-entry cache revision CAS가 성공할 때만 commit
  • cached value가 없으면 one-time unconditional request 또는 closed failure
  • app-managed conditional operation은 fetch cache mode를 no-store로 고정
  • full 200 mapped commit과 validator install/update는 같은 entry transaction
  • query removal/GC, scope/logout, release/contract/schema/mapper epoch reset에서 validator sidecar도 함께 폐기
  • ordinary invalidation에서 validator 보존 여부는 profile이 고정

BROWSER_HTTP_CACHE:

  • standard browser cache가 revalidation을 소유
  • application이 hidden validator/304 logic을 중복 구현하지 않음
  • HTTP Cache-Control을 TanStack staleTime으로 자동 변환하지 않음

write precondition:

  • exact resource revision을 If-Match 또는 body contract로 binding
  • 412는 PRECONDITION_FAILED
  • current server representation을 refetch한 뒤 feature use case가 overwrite, merge 또는 cancel을 결정
  • 409 business conflict와 합치지 않음

ETag/revision은 authorization proof가 아니며 raw value를 diagnostics에 넣지 않는다.

14. Schema와 Mapper 연결

VD-24의 typed codec/mapper를 사용한다.

unknown
  -> RuntimeCodec<ValidatedResponseDto>
  -> Mapper<ValidatedResponseDto, ApplicationProjection>
  -> Result

request는 strict/normalized parsed output만 serialize한다. ordinary additive response는 required/discriminant를 검증하고 unknown field를 폐기한다. control, authorization와 sealed union은 unknown을 거절한다.

generated OpenAPI client/DTO를 선택해도 adapter-private이다. handwritten gateway와 mapper를 제거하지 않는다.

15. Query cache와의 관계

REST transport는 raw response cache를 소유하지 않는다. VD-25가 mapped application projection을 TanStack Query에 admission한다.

  • operation registry의 cache profile만 query를 만들 수 있다.
  • Query retry는 false; REST transport가 network retry를 소유한다.
  • URL, ETag, idempotency key, Response와 DTO를 query key/value에 넣지 않는다.
  • mutation success 뒤 registered invalidation topic 또는 exact typed seed policy만 사용한다.
  • REST timeout/error를 Query가 다시 network retry하지 않는다.

16. Contract source와 release coherence

OpenAPI가 backend authority인 제품:

authenticated immutable OpenAPI artifact
  -> source digest/provenance
  -> lint + breaking diff
  -> pinned deterministic generation
  -> runtime codec parity
  -> adapter-private DTO/client
  -> handwritten mapper/gateway

CI:

  • generator/runtime/plugin/Node version pin
  • clean checkout regenerate diff 0
  • stable operation ID
  • source/artifact/generated output digest
  • runtime codec vs specification fixtures
  • N/N-1 and future-major failure
  • generated import boundary

global API_CONTRACT_VERSION은 selected contract-set compatibility와 digest에 연결한다. runtime config와 release manifest의 같은 문자열만으로 backend compatibility를 주장하지 않는다.

major version 전략은 URI version 또는 vendor media version 중 provider가 하나를 선택한다. 동시에 둘을 임의 증가시키지 않는다. v1/v2 adapters는 같은 application gateway를 구현할 수 있지만 같은 query entry에 representation을 섞지 않는다.

17. Security와 observability

관측 허용:

  • semantic operation/provider/profile ID
  • method/semantics
  • outcome/AppFailure kind/HTTP status group
  • logical retry, auth recovery와 physical attempt bucket
  • duration/deadline/request-response byte/item bucket
  • conditional/cache outcome

금지:

  • URL, path/search/header/body
  • cursor/snapshot/ETag/revision
  • idempotency/CSRF/auth token
  • raw backend code/message/request/trace value
  • application resource/account/tenant ID

client correlation ID는 bounded syntax로 request에 전달하고 server-projected request/trace ID는 safe failure/observation에만 제한한다. 한 logical execution은 terminal event 하나를 만든다.

18. Composition과 readiness

REST v2 composition owner가 다음을 atomic하게 만든다.

parse provider/config
  -> compose collision-free operation registry
  -> resolve codec/mapper/policy references
  -> install auth/CSRF owner
  -> run compatibility/provider probes
  -> publish READY facade

partial registry/client를 application에 노출하지 않는다.

Selection
TrafficAdmission
RuntimeHealth
PromotionEvidence

primary status가 COMPOSED여도 provider/auth/CSRF/idempotency/conditional conformance가 없으면 TrafficAdmission=DISABLEDPromotionEvidence=MISSING | PARTIAL | EXPIRED로 닫는다.

kill switch:

  • provider 전체
  • operation family
  • keyed retry/401 replay
  • conditional request
  • optimistic mutation
  • query cache admission

19. Test와 provider conformance

deterministic:

  • contribution collision, missing codec/mapper/profile와 owner mismatch
  • URI template/path/query canonicalization/base-prefix preservation
  • method/body/replay/status/media coherence
  • anonymous/cookie/bearer credentials와 Fetch cache mode matrix
  • auth final-request mutation 공격과 unavailable auth fetch 0
  • attempt timeout vs total deadline, timer/listener/reader cleanup
  • concurrent 401 single-flight
  • 401 replay를 포함한 monotonic physical-attempt cap
  • retry matrix, injected-clock 429/503 Retry-After
  • same key/same payload replay와 same key/different payload rejection
  • 204/304/412/422/problem/envelope
  • oversized/truncated/malformed/decompression overflow
  • cursor loop/snapshot/page ceiling
  • ETag 304 without cache, If-Match 412
  • mapper failure와 redaction

actual staging provider:

  • HTTPS/base path/CORS/preflight/credential
  • cookie/Origin/CSRF
  • idempotency concurrent claim/TTL/reconcile
  • status/media/error codec
  • cursor/snapshot/conditional semantics
  • rate limit/Retry-After
  • correlation/request/trace projection
  • proxy/CDN content encoding and body cap
  • browser decoded-byte cap과 provider encoded/decompression ceiling
  • outbound correlation, success status group와 401 physical-attempt observation

MSW 통과는 provider conformance가 아니다.

20. Rollout

  1. collision-aware v2 registry/codec과 boot-time binding 검증을 설치한다.
  2. auth fail-closed, final invariant, bounded decoder와 total deadline을 local reference vertical에서 검증한다.
  3. actual provider에 같은 fixture를 실행하고 read operation을 canary한다.
  4. keyed command는 backend idempotency conformance 뒤 별도 canary한다.
  5. pagination/conditional operation을 각각 별도 traffic gate로 올린다.
  6. provider/browser/operations evidence가 complete인 operation만 enabled한다.
  7. rollback은 우선 safe unavailable로 내리고 contract artifact/frontend/backend를 coherent set으로 복구한다. v1 fallback은 해당 operation의 unexpired provider/security evidence가 있고 incident가 v1/shared boundary에 영향이 없으며 auth fail-close/final invariant hardening이 유지될 때만 허용한다.

21. Removal

GraphQL/Connect/gRPC-Web/REST Gateway 선택을 취소해도 REST v2 common execution context는 남을 수 있다. REST provider 제거 시:

  1. 신규 operation admission 중지
  2. read cancel, command effect certainty reconcile
  3. auth/CSRF/retry timer와 response reader close
  4. current scope query cache clear/invalidate
  5. operation/schema/mapper/query profile 제거
  6. provider config/proxy/dependency/fixture 제거
  7. production module inventory와 backend route retirement evidence

완료 기준

  • installed REST operation이 typed path/search/body/success/error codec과 mapper에 하나의 definition으로 연결된다.
  • auth unavailable 또는 mutated final request에서 fetch가 0회다.
  • 모든 throw/response size/status/media/schema/mapper failure가 closed result다.
  • total logical deadline이 auth/recovery/backoff/decode/mapper를 포함한다.
  • replay는 declared policy와 actual backend idempotency evidence를 가진다.
  • complete cursor page와 conditional/412 state가 bounded하게 동작한다.
  • response DTO/URL/header/token/validator가 application/query/log에 없다.
  • actual provider conformance와 rollback/removal drill이 통과한다.