7.6 KiB
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/portscontracts: genuinely cross-capability shared vocabulary and registriesadapters: reusable capability runtimespresentation: generic UI/routing/query integrationbootstrap: concrete composition
Product business code is vertical:
features/<feature>/domainfeatures/<feature>/applicationfeatures/<feature>/contractsfeatures/<feature>/adaptersfeatures/<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/selectedinstalled-feature-contracts.ts: contract aggregationinstalled-feature-runtimes.tsx: runtime contribution aggregationinstalled-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:
useApplicationSessionuseApplicationPreferencesuseApplicationDiagnosticsuseApplicationRuntimeuseApplicationRecoveryuseApplicationFeature
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:
- domain type and invariant,
- use case,
- port,
- transport/storage schema plus mapper/codec,
- capability binding,
- 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.
Contract placement and the global-vs-owner-local audit are recorded in
contract-ownership.md.
Verification paths
Product-development verification, capability verification and release assurance
are intentionally separate. See
docs/testing/taxonomy.md.
Host-level CI-runner tests belong to tests/system, not tests/unit.
Reusable capability consumer contracts belong to tests/contract.