5.7 KiB
Capability consumer experience baseline
Correctness inside a reusable capability is not sufficient. The platform is also evaluated by how much of that correctness a normal feature developer must understand.
These measurements are baselines, not score targets. A lower line count is not automatically better if it hides business semantics or creates a universal repository abstraction.
Scenario A — reference REST feature
The existing reference feature is the executable REST consumer.
It covers list/detail/create behavior, mapping, typed operation inputs, application ports and an optimistic mutation path.
Current source size:
| Feature-owned area | LOC |
|---|---|
application/reference-feature-api.ts |
91 |
contracts/reference-mapper.ts |
109 |
adapters/reference-http-gateway.ts |
65 |
adapters/create-reference-feature-input.ts |
41 |
| Total measured boundary/application source | 306 |
The important boundary metric is not the raw total. It is what those files need to know about the platform.
Current result:
- HTTP platform imports in the feature adapter layer: 2 files,
- canonical import path used by both:
src/adapters/http/index.ts, - direct imports of
http-execution-v3.ts, retry scheduler, response reader, auth admission or effect-certainty internals: 0, - feature gateway owns transport failure projection: 0,
- feature gateway chooses operation ID, route ID, exact input/value type and mapper: yes,
- central installed files own feature-specific adapter/runtime wiring: no; feature-owned contributions are aggregated centrally.
The reusable HTTP capability now owns execution-outcome normalization through
createFeatureHttpBinding. A feature may still implement a custom outbound
adapter behind its application port when the reusable contract does not match
its business requirement.
Scenario B — IndexedDB local draft
The original executable consumer probe remains at:
tests/contract/consumer-experience/indexeddb-local-draft.test.ts
with its isolated fixture:
tests/contract/consumer-experience/fixtures/local-draft-feature.ts
The architecture is now also exercised by a real vertical slice under:
src/features/local-draft-feature
It owns the Local Draft domain/application API and binds an
IndexedDbRepositoryPort<LocalDraft, never> in its feature adapter. The
cross-capability composition contract lives at:
tests/contract/reusable-capability/feature-adapter-composition.test.ts
That test composes one HTTP-only contribution and the IndexedDB-only Local Draft contribution through the same generic catalog path.
The browser-level composition proof lives at:
tests/browser-capabilities/local-draft-composition.spec.ts
It runs against native browser IndexedDB. The test creates the platform
createIndexedDbRuntime, exposes that runtime through the typed
createIndexedDbRepositoryProvider, composes
LOCAL_DRAFT_FEATURE_ADAPTER_CONTRIBUTION, then executes Local Draft
save/find/remove through the feature API. The Local Draft feature still does
not import the IndexedDB runtime or native browser API.
Measured probe result:
| Metric | Result |
|---|---|
| feature-owned fixture LOC | 97 |
| platform import statements | 1 |
| native IndexedDB API references | 0 |
src/adapters/storage/indexeddb/** imports |
0 |
| runtime-internal IndexedDB types imported by feature | 0 |
The feature depends only on the public application boundary:
src/application/ports/browser-file-storage/index.ts
and specifically IndexedDbRepositoryPort<LocalDraft, never>.
The contract test rejects feature source that reaches for
globalThis.indexedDB, IDBFactory, IDBDatabase, IDBTransaction,
IDBObjectStore or the concrete IndexedDB adapter directory.
What this does and does not prove
The contract probe confirms that feature business code does not need native
IndexedDB knowledge once an IndexedDbRepositoryPort has been composed.
The browser-capability proof additionally confirms that the same feature
contribution works when that port is backed by the repository's real
createIndexedDbRuntime and native IndexedDB implementation.
It does not prove that composition of createIndexedDbRuntime is cheap.
That constructor still owns substantial infrastructure policy:
- dataset scope and storage policy,
- physical store governance,
- retention/idempotency stores,
- schema migrations,
- codec and query policy,
- lifecycle authority,
- durability, scheduling and observation.
That complexity belongs at the composition/platform boundary, not in the
feature. The Local Draft vertical slice is now the second concrete consumer, and
it confirms that the stable seam is the typed repository provider plus a
feature-owned repository identity. It does not show that dataset scope,
retention, migration, codec or lifecycle-authority configuration can be safely
collapsed into one universal create...Repository<T> factory.
A convenience profile should therefore be introduced only after another IndexedDB-backed product feature repeats the same infrastructure policy, not merely because two features consume the same repository port.
Consumer-quality review checklist
For each new product feature, record:
- feature-owned adapter LOC,
- platform glue LOC,
- files changed,
- central catalog edits,
- direct imports from capability-internal modules,
- native browser/network API references,
- duplicated failure/retry/lifecycle policy.
A healthy feature path should look like:
- domain type and invariant,
- use case,
- business port,
- feature-owned mapper/codec and policy,
- capability-specific binding,
- presentation controller/page.
The feature should not need the retry scheduler, abort ownership, effect-certainty machinery, IndexedDB transaction lifecycle, OPFS journal, reconnect coordinator or provider process model.