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:
@@ -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/
|
||||
|
||||
Reference in New Issue
Block a user