220 lines
7.6 KiB
Markdown
220 lines
7.6 KiB
Markdown
# 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`.
|