Three boundaries the layer contract declares had no executable rule behind them, so the code drifted across all three while every gate stayed green. `src/contracts` reached back up into `src/application` for the shared `Result` carrier and the compatibility predicate. Neither package owned the shared vocabulary and the dependency pointed both ways. Both now live in contracts — the lower package — and application re-exports them, so no caller moves. A concrete adapter was not supposed to depend on another concrete adapter, but only adapter-to-presentation was enforced, and `diagnostics` imported a guard out of `telemetry`. The guard belongs to neither, so it moved to the adapter kernel. Stating the rule needed the checker to resolve `$1` in a `to` pattern against the importing module's own directory; the alternative is one rule per adapter group, which silently stops covering a group the moment one is added. Product assembly leaks out of bootstrap: generic presentation reads the installed-feature registries. That is a real refactor, so the rule freezes the exact set of modules doing it today rather than pretending it is fixed — a new edge fails. The two remaining open edges are named in the config, not silent. Each rule was verified by introducing the violation it forbids and confirming the gate rejects it. The documentation drifted the same way. README and the manual accessibility checklist both said six routes while ten were registered, which left the platform overview and three reference-resource screens outside the declared manual review scope without anyone deciding they should be. The scope is now derived from the route registry by `verify:documentation`, so the sentence cannot outlive the registry again. The review ledger also named a canonical path that does not exist in this tree; it is upstream provenance, and it now says so instead of looking like a broken repository reference. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
169 lines
7.5 KiB
Markdown
169 lines
7.5 KiB
Markdown
# Clean Architecture Frontend Template
|
|
|
|
A React/Vite reference implementation where architecture boundaries,
|
|
integration behavior, release coherence, accessibility, performance, and
|
|
operations are executable contracts rather than conventions.
|
|
|
|
## Start locally
|
|
|
|
Requirements: the exact Node.js version in `.nvmrc` (currently 24.14.0) and
|
|
Corepack. The repository pins pnpm in `package.json`.
|
|
|
|
Product source, tests, build/quality scripts, and supported tool configuration
|
|
are TypeScript/TSX. `allowJs` is disabled. Node-side `.ts` scripts run directly
|
|
on the pinned Node 24 runtime and are checked with NodeNext resolution plus
|
|
erasable-syntax enforcement. Project-owned executable source contains no
|
|
JavaScript-family files; negative architecture, security, and type-compatibility
|
|
fixtures are TypeScript/TSX as well.
|
|
|
|
```bash
|
|
corepack pnpm install --frozen-lockfile
|
|
corepack pnpm dev
|
|
```
|
|
|
|
Runtime-public settings live in `public/config.json` and are validated before
|
|
the product tree mounts. Client secrets are forbidden.
|
|
|
|
## Included starter experience
|
|
|
|
The default build mounts a domain-neutral application shell with a header,
|
|
responsive sidebar, route focus management, session integration status, and a
|
|
persistent `system` / `light` / `dark` theme selector.
|
|
|
|
| Route | Purpose |
|
|
| --- | --- |
|
|
| `/` | implementation readiness and starter links |
|
|
| `/examples/ui` | buttons, fields, cards, alerts, badges, modal, and tokens |
|
|
| `/examples/states` | loading, refresh, empty, error, auth, forbidden, and not-found states |
|
|
| `/examples/auth` | reactive external-auth integration seam |
|
|
| `/examples/reference-resources` | removable, session-required reference feature |
|
|
|
|
`AUTH_MODE=demo` is credential-free and accepted only in local/development
|
|
environments. Deployments use `AUTH_MODE=external` and provide the opaque auth
|
|
owner described in
|
|
[`docs/architecture/starter-experience.md`](docs/architecture/starter-experience.md).
|
|
The client route policy is user experience only; server authorization remains
|
|
authoritative.
|
|
|
|
## Architecture
|
|
|
|
Dependencies point inward:
|
|
|
|
```text
|
|
presentation -> application -> domain
|
|
adapters -----^
|
|
bootstrap composes concrete adapters
|
|
contracts own cross-cutting registries
|
|
```
|
|
|
|
See `docs/architecture/overview.md`, `docs/architecture/layers.md`, and
|
|
`docs/architecture/starter-experience.md`. The removable vertical slice is
|
|
under `src/features/reference-feature`; its domain, application input, HTTP
|
|
adapter, contracts, route runtime, and presentation are installed through the
|
|
feature contribution files in `src/features`. The generic starter routes
|
|
continue to typecheck, test, and build after that contribution is removed.
|
|
|
|
### Platform capability review
|
|
|
|
The starter shell is implemented, but the repository review also records the
|
|
remaining work required before feature teams can use every declared contract
|
|
through one end-to-end application path:
|
|
|
|
- [platform capability review](docs/architecture/frontend-platform-capability-review.md)
|
|
- [ports, adapters, and feature boundaries](docs/architecture/frontend-ports-adapters-and-boundaries.md)
|
|
- [REST, GraphQL, Connect/gRPC-Web, Schema, Mapper, and Server State](docs/architecture/api-contract-schema-mapper-and-server-state.md)
|
|
- [Protobuf browser transports and REST Gateway](docs/architecture/protobuf-browser-transport-and-rest-gateway.md)
|
|
- [backend API and Server State handoff contract](docs/architecture/backend-api-and-server-state-contract.md)
|
|
- [TypeScript, state ownership, and data flow](docs/architecture/typescript-state-and-data-flow.md)
|
|
- [routing, page templates, and reusable patterns](docs/architecture/routing-pages-and-patterns.md)
|
|
- [browser data capability completion ledger](docs/architecture/browser-data-capability-completion-ledger.md)
|
|
- [browser file and origin-storage platform](docs/architecture/browser-file-and-origin-storage.md)
|
|
- [client cache and storage](docs/architecture/client-cache-and-storage.md)
|
|
- [realtime events, Web Push, and bounded polling](docs/architecture/realtime-events-web-push-and-bounded-polling.md)
|
|
- [presigned transfer, resumable upload, streaming download, and Image CDN](docs/architecture/presigned-transfer-and-image-cdn.md)
|
|
- [server file capability infrastructure](docs/architecture/server-file-capability-infrastructure.md)
|
|
- [design-system platform](docs/styling/design-system-platform.md)
|
|
- [frontend platform testing strategy](docs/testing/frontend-platform-testing-strategy.md)
|
|
- [implementation roadmap](docs/architecture/frontend-platform-implementation-roadmap.md)
|
|
|
|
These documents distinguish repository defaults from opt-in adapters and
|
|
project-owned integrations. They are target designs and review findings; a
|
|
capability is not treated as implemented until its branch acceptance criteria
|
|
and executable gates pass.
|
|
|
|
## Verification
|
|
|
|
Common local checks:
|
|
|
|
```bash
|
|
corepack pnpm lint
|
|
corepack pnpm check:types
|
|
corepack pnpm check:types:app
|
|
corepack pnpm check:types:node
|
|
corepack pnpm check:types:test
|
|
corepack pnpm check:architecture
|
|
corepack pnpm test:all
|
|
corepack pnpm test:e2e
|
|
corepack pnpm test:a11y
|
|
corepack pnpm build
|
|
corepack pnpm check:bundle
|
|
corepack pnpm test:performance
|
|
corepack pnpm verify:compatibility
|
|
corepack pnpm verify:release
|
|
corepack pnpm check:registries
|
|
corepack pnpm drill:runbooks
|
|
corepack pnpm check:ci
|
|
```
|
|
|
|
`check:types`는 source, Node scripts/config와 tests를 분리된 TypeScript
|
|
project로 모두 검사한다. type/architecture/security/registry의 invalid
|
|
fixture는 `config/ci/gates.json`에서 “실패해야 통과”하는 negative gate로
|
|
실행된다. 도구 호환성 결정은
|
|
[VD-01](docs/architecture/decisions/VD-01-typescript-lint-tooling.md)에 기록돼
|
|
있다.
|
|
|
|
Application feature input은 module augmentation으로 닫힌 ID와 정확한 input
|
|
shape를 제공하며, 공통 `Result<Value, Failure = AppFailure>`는 error registry의
|
|
failure kind만 application/presentation 경계를 통과시킨다. Architecture gate는
|
|
TypeScript/TSX의 static, dynamic, type import를 별도 정적 그래프로 분석하고
|
|
runtime/source 영역의 JavaScript 재유입도 거절한다. 해석되지 않은 import,
|
|
parse failure, 금지 계층 edge와 순환 의존은 모두 fail-closed이며 전용
|
|
TypeScript/TSX negative fixture로도 검증된다.
|
|
|
|
Install the pinned Playwright browser engines before the first cross-browser
|
|
run:
|
|
|
|
```bash
|
|
corepack pnpm exec playwright install --with-deps chromium firefox webkit
|
|
```
|
|
|
|
Two gates intentionally need external evidence:
|
|
|
|
- `review:a11y-manual` needs a signed human keyboard/focus/screen-reader review
|
|
for all ten registered routes: `APP_HOME`, `EXAMPLES_PLATFORM`,
|
|
`EXAMPLES_UI`, `EXAMPLES_STATES`, `EXAMPLES_AUTH`, `NOT_FOUND`,
|
|
`REFERENCE_RESOURCE_LIST`, `REFERENCE_RESOURCE_DETAIL`,
|
|
`REFERENCE_RESOURCE_FORM` and `REFERENCE_RESOURCE_STATUS`.
|
|
`verify:documentation` derives that list from the route registry and fails if
|
|
this paragraph falls behind it.
|
|
- `collect:web-vitals-evidence` stays `FAIL_UNVERIFIED` until a reviewed minimum
|
|
eligible-sample threshold and 28 days of production data exist.
|
|
|
|
Live release verification additionally requires `HOSTING_BASE_URL`.
|
|
|
|
## CI and evidence
|
|
|
|
The 26-gate registry is `config/ci/gates.json`; the Gitea workflow is
|
|
`.gitea/workflows/quality-gates.yml`. It follows:
|
|
|
|
```text
|
|
MERGE_READY -> RELEASE_READY -> PROD_PROMOTION_READY -> FIELD_SLO_READY
|
|
```
|
|
|
|
`DOCUMENTATION_READY` is independent. No gate is downgraded to a warning.
|
|
Machine-readable evidence is written below `artifacts/`; generated evidence is
|
|
ignored by Git while `.gitkeep` files preserve the taxonomy.
|
|
|
|
Operational details are in `docs/operations/`, with incident procedures in
|
|
`docs/runbooks/`.
|