refactor: 프론트 템플릿 리펙토링

This commit is contained in:
donghyeon-ka
2026-09-18 15:16:58 +09:00
parent c10a709f2c
commit 5cc41467ae
80 changed files with 7227 additions and 4672 deletions
@@ -0,0 +1,219 @@
# Frontend Application Foundation
This repository is not treated as a minimal React project template and it is not
an independent general-purpose SDK. Its architectural role is:
> Frontend Application Foundation = Starter / Composition Skeleton + Reusable Capability Platform
The starter side owns bootstrap, routing, providers, project conventions and a
removable reference feature. The capability side owns reusable technical
problems such as HTTP execution, Server State, authentication boundaries,
IndexedDB/OPFS, Cache Storage, realtime, browser RPC, transfer and diagnostics.
The cost of a sophisticated capability is acceptable only when product features
do not have to understand that internal sophistication.
## Hybrid architecture
Platform code is horizontal:
- `application`: generic application inputs/policies/ports
- `contracts`: genuinely cross-capability shared vocabulary and registries
- `adapters`: reusable capability runtimes
- `presentation`: generic UI/routing/query integration
- `bootstrap`: concrete composition
Product business code is vertical:
- `features/<feature>/domain`
- `features/<feature>/application`
- `features/<feature>/contracts`
- `features/<feature>/adapters`
- `features/<feature>/presentation`
The dependency model is:
Domain / Use case
|
| owns
v
Business port
^
| implements / binds
|
Feature-owned adapter binding
|
| generic type + mapper/codec + policy
v
Reusable capability runtime
|
v
Browser / network / native API
A use case never imports a platform adapter. Generic binding happens in the
feature adapter/composition seam.
## Capability-specific typed bindings
Do not introduce one universal `Repository<TKey, TValue>` abstraction for HTTP,
storage, realtime and transfer. Their lifecycle and failure semantics differ.
A reusable capability boundary is composed from:
- generic input/output types,
- feature-owned mapper or codec,
- feature-selected policy,
- one capability-specific runtime.
The HTTP reference path is the first concrete example.
`src/adapters/http/feature-http-binding.ts` owns transport/outcome
normalization. The reference feature contributes only:
- operation ID,
- route ID,
- exact request input type,
- exact success value type,
- wire-to-domain mapper.
The feature gateway therefore does not reimplement timeout, cancellation,
transport failure, authentication failure or contract-violation projection.
Storage, realtime and transfer may gain their own typed binders only after
actual feature repetition demonstrates the need. They must not be forced
through the HTTP abstraction.
## Custom adapter escape hatch
A product feature uses a reusable capability when the capability preserves the
business requirement.
If a platform contract would require changing or weakening the business model,
the feature implements a custom outbound adapter behind the same application
port. The architecture boundary remains stable; platform reuse is optional.
## Feature installation
A feature owns its contract, runtime and adapter contributions.
Central installed catalogs are aggregation points only:
- `installed-product-manifest.ts`: which product features are compiled/selected
- `installed-feature-contracts.ts`: contract aggregation
- `installed-feature-runtimes.tsx`: runtime contribution aggregation
- `installed-feature-adapters.ts`: application-input contribution aggregation
Feature-specific composition belongs under the feature itself. Central
catalogs must not grow feature-specific branching logic.
## Presentation consumer surface
`ApplicationProvider` remains the composition root for presentation, but new
consumers should not navigate a root `ApplicationApi` service locator.
Use the narrow hooks in
`src/presentation/providers/application-provider.tsx`:
- `useApplicationSession`
- `useApplicationPreferences`
- `useApplicationDiagnostics`
- `useApplicationRuntime`
- `useApplicationRecovery`
- `useApplicationFeature`
`useApplication` exists only as a deprecated compatibility escape hatch.
## Canonical imports
New feature code should use capability public entry points rather than deep
runtime modules. For HTTP the canonical path is
`src/adapters/http/index.ts`.
Compatibility re-exports may exist during a migration window, but they must be
marked as compatibility/deprecated paths and should not expand into an
unbounded public barrel.
## Policy ownership
Duplication is judged by ownership, not by syntax percentage.
Small local validators can remain duplicated when locality improves auditing.
Business or concurrency policy must have one owner. For example the reference
create mutation keeps definition ID, idempotency requirement, duplicate policy
and invalidation policy in one feature-owned definition and binds only
`scope` and `execute` per usage site.
## Runtime decomposition rule
Large runtime files are not split by line count.
Extract a boundary when it has its own state machine, lifecycle owner, failure
model or compensation/recovery responsibility. Candidate seams include:
- connection/open/upgrade lifecycle,
- transaction ownership,
- migration state machine,
- reconnect/backoff and heartbeat,
- subscription ownership,
- retry/deadline/cancellation ownership,
- settlement/reconciliation/cleanup.
A cohesive 2,000-line state machine can remain together. A 300-line file with
multiple lifecycle owners is a better extraction candidate.
### Current runtime boundary audit
The current large-runtime inventory was reviewed using that rule.
- IndexedDB remains large, but connection, transaction, migration, maintenance
and failure translation already have separate owners/modules.
- resumable upload already separates runtime policy, checkpoint persistence,
HTTP control-plane transport, part execution, cancellation and mutation
locking.
- realtime already separates reconnect policy/coordinator, event codec/consumer
and stream coordination.
- OPFS is separated into browser runtime, journal, byte-store, policy and worker
protocol/runtime responsibilities.
- HTTP V3 still owned retry eligibility/backoff inside the execution state
machine, so that responsibility moved to
`src/adapters/http/http-retry-lifecycle.ts`.
No other runtime is split merely because of its line count.
## Consumer quality metrics
Before adding another abstraction, implement or model multiple real feature
uses and measure:
- feature-owned adapter LOC,
- repeated platform glue,
- number of platform-internal types exposed to the feature,
- central catalog edits,
- files changed for one normal query/command,
- whether native browser/network APIs leak into the feature.
The target feature-development path is:
1. domain type and invariant,
2. use case,
3. port,
4. transport/storage schema plus mapper/codec,
5. capability binding,
6. presentation controller/page.
A product feature should not need to know the retry scheduler, abort ownership,
effect-certainty machinery, transaction leases, reconnect coordinator, OPFS
journal or provider lifecycle.
The executable REST and IndexedDB consumer baselines are recorded in
[`capability-consumer-experience.md`](./capability-consumer-experience.md).
Contract placement and the global-vs-owner-local audit are recorded in
[`contract-ownership.md`](./contract-ownership.md).
## Verification paths
Product-development verification, capability verification and release assurance
are intentionally separate. See
[`docs/testing/taxonomy.md`](../testing/taxonomy.md).
Host-level CI-runner tests belong to `tests/system`, not `tests/unit`.
Reusable capability consumer contracts belong to `tests/contract`.