ADR 0056 — Entity cache shapes are declared
- Status: Accepted — extends the optional cache bridge without turning it into a domain cache framework
- Date: 2026-08-07
Context
The original entity helper understood only Paginated<T> and
InfiniteData<Paginated<T>>, assumed list rows were full entities, used one
static list-key prefix and prepended every create. Real query caches also use
plain arrays and array-valued infinite pages. Scoped keys, projected list rows
and backend ordering therefore required a second hand-written CRUD updater.
Those differences are application policy. Inferring a workspace from a key, deriving sort order from fields or rewriting totals would make the helper domain-aware and silently corrupt cache metadata.
Decision
Keep one createEntityCacheHandlers<TData, TListItem> API. Its list config
declares one of four shapes: array, paginated, infinite array pages or infinite
paginated pages. A built-in discriminant is sufficient; a separate public list
adapter would expose more machinery without expressing another behavior.
The config explicitly provides full-entity identity, list-item identity and projection. Static keys remain the short path. Dynamic list/detail keys receive one discriminated created/updated/deleted event, including the canonical id and the original entity or deleted payload.
createAt selects the insertion edge/page. updateMissing explicitly chooses
skip or insert. An optional comparator is application-owned and applied after
create/update; Stitchkit never guesses backend ordering.
All operations preserve outer objects, nextCursor, page-specific metadata
and pageParams. Infinite insertion changes only the selected edge page.
Create deduplication and update/delete matching inspect every cached page.
Runtime shape checks leave other query values under an intentionally partial
list-key prefix untouched.
Alternatives rejected
- A generic read/write list adapter callback. It exposes TanStack internals in every consumer config and becomes callback soup for four fixed shapes.
- Keep the old helper beside a new one. Two CRUD semantics would drift and make migrations ambiguous.
- Infer shape from cached values. Empty/missing caches are ambiguous and a malformed neighbouring query under a prefix would be patched incorrectly.
- Update totals or cursor metadata. Their meaning is endpoint-specific and cannot be derived from a socket event safely.
Consequences
- One small declaration covers the representative real cache shapes.
- Projection and sorting remain typed and owned by the application.
- The config is intentionally more explicit; this is a pre-1.0 breaking change with one clean path and no compatibility shim.
- Arbitrary cache transformations continue to use TanStack Query directly.