InternalArchitecture
Performance via Batching
Solve N+1 by batching reads per tick/RAF without coupling to UI frameworks.
13. Batching (performance without magic)
13.1 The problem
Normalized access patterns cause many reads:
getInquiry(id);
getCustomer(customerId);
getAssignee(assigneeId);Naively, this is N+1 DB queries.
13.2 The solution: batch loader
We batch reads, not invalidations.
13.3 Batch loader contract
interface BatchLoader<K, V> {
load(key: K): Promise<V | null>;
}Behavior:
- collects keys during a tick / RAF
- deduplicates
- executes one
WHERE id IN (...) - splits results back to callers
13.4 Critical property
The batch loader:
- does not know about TanStack Query
- does not know about UI
- lives in the data access layer
Any caller benefits.
13.5 Implementation
@rippledb/client-controllers provides a reference implementation of the batch loader pattern on top of the Store interface. It automatically uses Store.getRows() when available (optimized path) or falls back to parallel getRow() calls.
The client-controllers package also provides entity controllers that combine batch loading with CRUD operations, making it easy to build data access layers that benefit from automatic batching.