docs(hub): port 12개, adapter 책임 10행, capability boot 단계, 디렉터리 확장

- 멀티프로토콜은 기존 ResourceQueryPort/ResourceCommandPort 를 재사용해
  신규 port 0개. 프로토콜은 registry 데이터이지 타입이 아니다.
- Image CDN 은 I-O 가 없으므로 adapter 가 아니라 application 정책.
- boot 에 capability 해석 단계 삽입 — 실패하지 않고, 비활성 adapter 는
  정적 import 하지 않는다(FE-NFR-020 방어).
- worker/SW 엔트리는 별도 실행 컨텍스트라 상위 layer 를 import 하지 않는다.
This commit is contained in:
DongHyeonka
2026-07-28 14:14:45 +09:00
parent a8804cd9f2
commit 7a7d79b749
@@ -521,7 +521,17 @@ Out of scope:
| `adapters/storage` | storage port implementation, serialization, quota mapping | application port, Web Storage | token lifecycle, domain policy | `planned` |
| `adapters/telemetry` | telemetry port implementation, queue, redaction, sink | application port, browser transport | UX decision, navigation | `planned` |
| `adapters/query-cache` | application-owned `QueryCachePort` implementation, TanStack Query key/invalidation bridge | application port, TanStack Query | use-case policy, page-local query key | `planned` |
| `bootstrap` | config load, adapter construction, dependency injection, React mount | all runtime modules | business rule, page-specific orchestration | `planned` |
| `adapters/capability` | capability flag 해석, 브라우저 feature detection, 비활성 사유 | application port, runtime config, browser globals | 활성화 여부의 제품 판단 | `planned` |
| `adapters/file` | 파일 선택·저장 dialog, object URL 생성/해제 | application port, File System Access / input element | 파일 내용 해석, 도메인 검증 | `planned` |
| `adapters/blob-store` | IndexedDB·OPFS·Cache Storage 백엔드, quota 매핑, eviction | application port, browser storage API | 저장 대상의 의미, 도메인 정책 | `planned` |
| `adapters/cache-persistence` | 캐시 직렬화, version partition, 복원 거부 | application port, 자체 영속 백엔드 | 캐시 정책 결정(`QueryCachePort` 소유) | `planned` |
| `adapters/cross-tab` | BroadcastChannel·`storage` event 전송, 메시지 봉투 | application port, browser globals | 무효화 대상 결정 | `planned` |
| `adapters/transfer` | part 분할·병렬·재시도·무결성·진행 보고, credential-less 전송 | application port, fetch, Streams | presign 발급, 업로드 대상 도메인 규칙 | `planned` |
| `adapters/protocol` | GraphQL·gRPC-Web·Connect-Web codec 과 status 정규화 | application output port, fetch | operation 정의, use-case policy | `planned` |
| `adapters/realtime` | 연결 수명주기, 재연결 backoff, resume cursor, 프레임 디코드, 구독 해제 | application port, EventSource·WebSocket·fetch | 이벤트의 도메인 의미, 상태 병합 정책 | `planned` |
| `adapters/worker` | worker 생성·통신·timeout·terminate | application port, Worker API | 도메인 계산 규칙 | `planned` |
| `adapters/service-worker` | SW 등록·갱신 상태, background sync 큐 등록 | application port, ServiceWorker API | precache 정책 결정(`FE-D034` 소유), mutation 의미 | `planned` |
| `bootstrap` | config load, capability 해석, adapter construction, dependency injection, React mount | all runtime modules | business rule, page-specific orchestration | `planned` |
### 4.3 Dependency matrix
@@ -545,6 +555,10 @@ Normative dependency summary:
- adapter는 application port를 구현하지만 application은 adapter 이름을 알면 안 된다.
- presentation은 application facade를 호출하며 raw backend envelope를 직접 다루면 안 된다.
- bootstrap만 concrete adapter를 조립할 수 있다.
- `adapters/transfer``UploadTransferPort` 입력으로 **바이너리 descriptor**를 받는다. `adapters/file`·`adapters/blob-store`의 concrete 모듈을 import하지 않으며, descriptor 조립은 `bootstrap` 또는 application orchestration이 한다 (`DELEG-FE-008`).
- `adapters/realtime/push``ServiceWorkerHostPort` **인터페이스**를 통해 SW 등록 상태를 읽는다. `adapters/service-worker`의 concrete 모듈을 import하지 않는다 (`DELEG-FE-007`).
- `src/workers/*.worker.js``public/sw.js`는 **별도 실행 컨텍스트**이므로 `presentation`·`application`·`domain`을 import하지 않는다. 공유가 필요하면 `domain`의 순수 모듈만 참조하고 그 사실을 `FE-REG-CAPABILITY` 행에 기록한다.
- worker 엔트리는 `adapters/worker`가 소유한다. `bootstrap`이 직접 `new Worker()`를 부르지 않는다.
### 4.4 Port ownership matrix
@@ -558,9 +572,25 @@ Normative dependency summary:
| `TelemetryPort` | `application` | `adapters/telemetry` | application + boundary | sanitized event → best-effort ack | `TelemetryDropped` internal only |
| `ClockPort` | `application` | browser/system clock adapter | retry/release logic | now / monotonic duration | no user-facing error |
| `ReleaseInfoPort` | `application` | runtime config/release adapter | boot + chunk recovery | release manifest → compatible release info | `RELEASE_MANIFEST_FAILURE`, `DEPLOY_MISMATCH` |
| `CapabilityPort` | `application` | `adapters/capability` | bootstrap + capability 소비 use case | capability ID → 활성 여부 + 사유 | `CAPABILITY_DISABLED`, `CAPABILITY_UNSUPPORTED` |
| `FileDialogPort` | `application` | `adapters/file` | 파일 입출력 use case | 선택 제약(accept·multiple·max) → 파일 descriptor 목록 / 저장 요청 → 저장 결과 | `FILE_PICKER_DISMISSED`, `FILE_REJECTED` |
| `BlobStorePort` | `application` | `adapters/blob-store` | 로컬 바이너리 보관 use case | 등록 key + 바이너리 descriptor → 저장/조회/삭제 결과 | `BLOB_STORE_UNAVAILABLE`, `BLOB_STORE_QUOTA_EXCEEDED` |
| `CachePersistencePort` | `application` | `adapters/cache-persistence` | `QueryCachePort` 구현 보조 | 캐시 스냅샷 + version tuple → 영속/복원 결과 | `CACHE_PERSISTENCE_FAILURE` |
| `CrossTabSyncPort` | `application` | `adapters/cross-tab` | 캐시 무효화 orchestration | 무효화 key 메시지 → 발행/수신 구독 | `CROSS_TAB_CHANNEL_UNAVAILABLE` |
| `UploadTransferPort` | `application` | `adapters/transfer/upload` | 업로드 use case | 전송 계획(source descriptor + presign 결과) → 진행 스트림 + 완료 결과 | `PRESIGN_EXPIRED`, `UPLOAD_PART_FAILED`, `TRANSFER_INTEGRITY_MISMATCH` |
| `StreamingDownloadPort` | `application` | `adapters/transfer/download` | 다운로드 use case | operation + range/resume 위치 → 진행 스트림 + 완료 결과 | `STREAM_INTERRUPTED` |
| `RealtimeSubscriptionPort` | `application` | `adapters/realtime/{sse,websocket,polling}` | 스트림 소비 use case | 구독 descriptor + resume cursor → 검증된 이벤트 스트림 + 해제 handle | `REALTIME_CONNECT_FAILED`, `REALTIME_DISCONNECTED`, `REALTIME_RESUME_GAP`, `EVENT_SCHEMA_MISMATCH` |
| `PushSubscriptionPort` | `application` | `adapters/realtime/push` | 알림 등록 use case | 권한 요청 + 공개키 → 구독 descriptor | `PUSH_PERMISSION_DENIED`, `PUSH_SUBSCRIPTION_EXPIRED` |
| `WorkerTaskPort` | `application` | `adapters/worker` | CPU 오프로드 use case | task 이름 + 직렬화 가능 입력 + timeout → 결과 또는 종료 | `WORKER_UNAVAILABLE`, `WORKER_TASK_TIMEOUT` |
| `ServiceWorkerHostPort` | `application` | `adapters/service-worker` | bootstrap + release 감시 | 등록 요청 → 등록/갱신 상태 스트림 | `SW_REGISTRATION_FAILED` |
| `BackgroundSyncPort` | `application` | `adapters/service-worker/sync` | 지연 mutation use case | keyed mutation descriptor → 큐 등록 결과 | `BACKGROUND_SYNC_UNSUPPORTED`, `BACKGROUND_SYNC_REPLAY_REJECTED` |
`AuthSessionPort`는 token 문자열을 domain/application model로 반환하지 않는 형태를 우선한다. header supplier나 opaque credential attachment callback을 사용하고, 구현 세부는 auth owner가 정한다.
**멀티프로토콜(`FE-OC-030`)은 신규 port가 0개다.** GraphQL·gRPC-Web·Connect-Web은 기존 `ResourceQueryPort`/`ResourceCommandPort`의 다른 **구현체**다. 프로토콜이 application에 새 인터페이스로 새면 `FE-D010`의 dependency inversion이 무너지고, backend가 REST에서 gRPC로 옮겨갈 때 use case를 다시 써야 한다. 프로토콜 선택은 `FE-REG-API.protocol` 필드 = **데이터**이지 타입이 아니다.
**Image CDN은 adapter가 아니라 `application`의 순수 정책이다.** URL 파생에는 I-O가 없다. `MediaUrlPolicy``application/policies/`에 두고 CDN base·허용 transform은 `FE-REG-ENV` 행에서 읽는다. port로 만들면 테스트에 불필요한 test double만 늘어난다.
### 4.5 Composition root
Planned location:
@@ -577,14 +607,21 @@ Boot order는 다음을 `MUST` 따른다.
3. config envelope·schema·compatibility 검증
4. release manifest 정합성 확인
5. registry snapshot load
6. auth integration adapter 주입
7. HTTP/storage/telemetry/query-cache adapter 생성
8. application facade 생성
9. router 생성
10. React root mount
6. capability 해석 — flag × 브라우저 feature detection → 활성 capability 집합 확정
7. auth integration adapter 주입
8. HTTP/storage/telemetry/query-cache adapter 생성
9. 활성 capability 의 adapter 생성 (비활성 capability 의 adapter 는 생성하지 않음)
10. application facade 생성
11. router 생성
12. React root mount
2~4단계가 실패하면 product route를 mount하지 않고 boot error shell만 렌더한다. telemetry adapter 생성 실패는 console-safe fallback으로 계속 진행할 수 있다.
6단계와 9단계는 다음을 `MUST` 지킨다.
- 6단계는 실패하지 않는다. flag가 ON인데 브라우저가 미지원이면 해당 capability를 비활성으로 확정하고 `capability.activation.rejected` telemetry를 남긴 뒤 boot를 계속한다. capability 부재가 boot를 막으면 skeleton이 특정 브라우저에 묶인다.
- 9단계는 비활성 capability의 adapter 모듈을 **정적 import하지 않는다**. 정적 import하면 `FE-NFR-020`(비활성 시 initial JS 증가 0)이 깨지고 `FE-GATE-033`이 실패한다.
### 4.6 Planned directory blueprint
```text
@@ -600,6 +637,7 @@ src/
ports/
use-cases/
view-models/
policies/
presentation/
app/
routes/
@@ -613,6 +651,26 @@ src/
query-cache/
auth/
release/
capability/
file/
blob-store/
cache-persistence/
cross-tab/
transfer/
upload/
download/
protocol/
graphql/
grpc-web/
connect-web/
realtime/
sse/
websocket/
polling/
push/
worker/
service-worker/
sync/
contracts/
routes.js
api-operations.js
@@ -622,8 +680,12 @@ src/
query-keys.js
telemetry.js
release-tokens.js
capabilities.js
workers/
sample/
contract-fixture/
public/
sw.js
tests/
unit/
component/