|
| 1 | +# Architectural Plan: The Unified Reactive List |
| 2 | + |
| 3 | +**Status:** Draft |
| 4 | +**Date:** January 17, 2026 |
| 5 | +**Context:** Merging the "Smart List" capabilities of legacy `createListApi` with the "Thermodynamic Model" architecture of `core-experimental`. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 1. Executive Summary |
| 10 | + |
| 11 | +Our research has identified a gap in the current `core-experimental` architecture. While `keyval` excels at managing the lifecycle and topology of polymorphic **Models** (Entities), it lacks the sophisticated list management capabilities (Filtering, Mapping, Path-based Updates) found in our legacy `createListApi` implementation. |
| 12 | + |
| 13 | +This plan proposes a unified architecture that layers a **Query Engine** (ListApi) on top of the **Storage Engine** (Keyval), providing the best of both worlds: highly efficient entity management with ergonomic list operations. |
| 14 | + |
| 15 | +## 2. The Architecture: Storage vs. View |
| 16 | + |
| 17 | +We propose strictly separating the **Data Plane** (Storage) from the **Presentation Plane** (View). |
| 18 | + |
| 19 | +### 2.1. Layer 1: The Storage Engine (`keyval`) |
| 20 | + |
| 21 | +_Responsibility: Lifecycle, Persistence, Topology._ |
| 22 | + |
| 23 | +The current `keyval` implementation remains the foundation. It manages: |
| 24 | + |
| 25 | +- **`$instances`**: A Record of active Model instances (Scopes). |
| 26 | +- **`$state`**: A serialized snapshot of the data. |
| 27 | +- **`lifecycle`**: Creating and destroying scopes based on ID presence. |
| 28 | + |
| 29 | +**Improvements needed:** |
| 30 | + |
| 31 | +- **`sync(Store<T[]>)`**: Ability to synchronize the order and existence of items from an external source (e.g., Server Response), replacing the manual `add/remove` logic. |
| 32 | +- **`update(id, path, value)`**: A generic update method that uses path string/array to modify deep state, reducing boilerplate. |
| 33 | + |
| 34 | +### 2.2. Layer 2: The Query Engine (`ListApi`) |
| 35 | + |
| 36 | +_Responsibility: Sorting, Filtering, Projection._ |
| 37 | + |
| 38 | +This is the new layer inspired by `createListApi`. It consumes a `keyval` and produces a derived **View**. |
| 39 | + |
| 40 | +```typescript |
| 41 | +// Definition |
| 42 | +const allUsers = keyval({ model: UserModel }); |
| 43 | + |
| 44 | +// Derived View (Reactive) |
| 45 | +const admins = allUsers.view() |
| 46 | + .filter((user) => user.input.role === 'admin') |
| 47 | + .sort((a, b) => a.input.name.localeCompare(b.input.name)); |
| 48 | + |
| 49 | +// Consumption |
| 50 | +useList(admins, (user) => <UserCard model={user} />); |
| 51 | +``` |
| 52 | + |
| 53 | +**Key Features:** |
| 54 | + |
| 55 | +1. **`$visibleKeys`**: A store containing only the IDs that match the filter. |
| 56 | +2. **Virtualization Support**: The View only tracks IDs, preventing render churn for items that are filtered out. |
| 57 | +3. **Chainable API**: `filter().sort().map()` creates a pipeline of derived stores. |
| 58 | + |
| 59 | +## 3. Proposed API Specification |
| 60 | + |
| 61 | +### 3.1. Enhanced `Keyval` |
| 62 | + |
| 63 | +```typescript |
| 64 | +type Keyval<M> = { |
| 65 | + // ... existing fields ... |
| 66 | + |
| 67 | + // New: Path-based update (inspired by legacy set) |
| 68 | + set: (id: string, path: string, value: any) => void; |
| 69 | + |
| 70 | + // New: Create a derived View |
| 71 | + view: () => ListApi<M>; |
| 72 | + |
| 73 | + // New: Synchronization (inspired by createStoreMap) |
| 74 | + sync: (source: Store<any[]>, getKey: (item: any) => string) => void; |
| 75 | +}; |
| 76 | +``` |
| 77 | + |
| 78 | +### 3.2. `ListApi` (The View) |
| 79 | + |
| 80 | +```typescript |
| 81 | +type ListApi<M> = { |
| 82 | + $items: Store<string[]>; // Filtered & Sorted IDs |
| 83 | + |
| 84 | + // Refines the view |
| 85 | + filter: (fn: (instance: LensProxy<M>) => boolean | Store<boolean>) => ListApi<M>; |
| 86 | + sort: (fn: (a: LensProxy<M>, b: LensProxy<M>) => number) => ListApi<M>; |
| 87 | + |
| 88 | + // Returns the subset of instances |
| 89 | + use: () => LensProxy<M>[]; |
| 90 | +}; |
| 91 | +``` |
| 92 | + |
| 93 | +## 4. Implementation Strategy |
| 94 | + |
| 95 | +### Phase 1: Storage Improvements |
| 96 | + |
| 97 | +1. **Implement `keyval.set`**: modify `updateInstanceFx` to accept a path array (e.g., `['facets', 'product', '$price']`) and traverse the instance to find the store to `rehydrate`. |
| 98 | +2. **Implement `keyval.sync`**: Create logic that watches an external array store. |
| 99 | + - **Diffing**: Calculate added/removed IDs. |
| 100 | + - **Reordering**: Update `$items` order to match source. |
| 101 | + - **Garbage Collection**: Call `destroy()` on removed IDs. |
| 102 | + |
| 103 | +### Phase 2: Query Engine |
| 104 | + |
| 105 | +1. **Implement `createListView(keyval)`**: |
| 106 | + - Create `$filter` store. |
| 107 | + - Derive `$filteredIds` from `keyval.$items` + `$filter` + `keyval.$instances`. |
| 108 | + - **Optimization**: Use `shouldNotify` logic (from legacy code) to avoid re-calculating filter if only unrelated data changed. |
| 109 | + |
| 110 | +### Phase 3: Developer Experience |
| 111 | + |
| 112 | +1. **Typed Paths**: Use TypeScript Template Literal Types to auto-complete paths in `.set()`. |
| 113 | + - `cart.set('id', 'facets.product.$quantity', 5)` |
| 114 | + |
| 115 | +## 5. Comparison with Legacy Code |
| 116 | + |
| 117 | +| Feature | Legacy `createStoreMap` | Legacy `createListApi` | New `keyval` + `ListApi` | |
| 118 | +| :------------------ | :---------------------- | :----------------------- | :-------------------------- | |
| 119 | +| **Source of Truth** | Map (Derived) | List + Map (Stand-alone) | Keyval (Storage) | |
| 120 | +| **Order** | Manual Sync | Managed Array | Managed Array | |
| 121 | +| **Updates** | `setState` (Manual) | `set(path)` (Smart) | `set(path)` (Smart) | |
| 122 | +| **Filtering** | N/A | Native `$filter` | Native `.view().filter()` | |
| 123 | +| **Typing** | Manual | Manual | **Fully Inferred (Models)** | |
| 124 | + |
| 125 | +## 6. Conclusion |
| 126 | + |
| 127 | +By integrating the "Smart List" features into the "Thermodynamic" architecture, we create a system that is not only performant (memory efficient) but also ergonomic for complex UI requirements (filtering/sorting). The distinction between **Storage** (Backend state) and **View** (UI state) is the critical architectural leap. |
0 commit comments