Architecture
Understand how SoulCache is designed and built
Architecture
SoulCache is designed with a modular, layered architecture that separates concerns while maintaining high performance.
High-Level Overview
Core Components
QueryClient
The central orchestration layer that owns all major subsystems:
- Request deduplication via
_pendingFetchesMap - Query lifecycle management via QueryStateMachine instances
- Observer coordination via QueryObserver instances
- Mutation execution via MutationCache
- Event emission via EventBus
- Task scheduling via Scheduler
QueryEngine
QueryEngine is a higher-level wrapper around QueryClient that adds retry support (via RetryEngine), background refetch intervals, and stale-time-aware cache reads. Use QueryClient for direct control, or QueryEngine for automatic retry and polling.
CacheEngine
In-memory cache with O(1) Map-based lookups:
- Automatic garbage collection based on
gcTime - Stale-while-revalidate via
staleTime - LRU eviction for memory management
- Dependency tracking for invalidation propagation
- Version tracking for consistency
MutationCache
Manages mutation lifecycle and optimistic updates:
- MutationEntry creation and tracking
- Optimistic update context management
- Simple retry via MutationEntry.mutateWithRetry()
- State machine for mutation states (idle → pending → success/error)
Scheduler
Priority-based task execution engine:
- Priority levels: critical > high > normal > low > idle
- Batch execution with observer notification deduplication
- Task dependency resolution with circular dependency detection
EventBus
Internal typed event system for module communication:
- FIFO event delivery
- Typed subscriptions with event log
- Decouples modules without direct dependencies
Query Execution Flow
Module Map
| Module | Key Classes | Exported |
|---|---|---|
client/ | QueryClient | Yes |
query/ | QueryEngine, QueryStateMachine | Both |
cache/ | CacheEngine, QueryEntry | Yes |
mutation/ | MutationEntry, MutationCache, MutationObserver | Yes |
infinite/ | InfiniteQuery | Yes |
retry/ | RetryEngine | No (internal) |
scheduler/ | Scheduler, createTask | Yes |
storage/ | StorageManager, MemoryAdapter, PersistenceCoordinator | Yes |
events/ | EventBus | Yes |
observer/ | QueryObserver | QueryObserver only |
plugin/ | PluginManager | No (internal) |
hydration/ | dehydrate, hydrate, serialize, deserialize | Yes |
subscription/ | SubscriptionManager | Yes |
snapshot/ | QuerySnapshotManager | Yes |
fetcher/ | Fetcher | No (internal) |
Packages
| Package | Purpose |
|---|---|
@soulcache/core | Core runtime with all modules |
@soulcache/react | React hooks via useSyncExternalStore |
@soulcache/devtools-core | Framework-agnostic inspection layer |
@soulcache/devtools | React DevTools panel |
Design Principles
Separation of Concerns
Each module has a single responsibility:
- CacheEngine: In-memory storage and eviction only
- QueryClient: Orchestration and deduplication only
- Scheduler: Task execution and batching only
- EventBus: Cross-module communication only
Zero Runtime Dependencies
SoulCache has no runtime dependencies. All functionality is built from scratch for:
- Minimal bundle size
- No supply chain risks
- Full control over behavior
Framework Independence
The core runtime is framework-agnostic. The React adapter provides hooks built on useSyncExternalStore for React 18+ concurrent mode compatibility.
Type Safety
End-to-end TypeScript with:
- Strict mode enabled
- Minimal
anyusage in source - Full inference support
- Comprehensive type exports
Performance Characteristics
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Cache Get | O(1) | O(1) |
| Cache Set | O(1) | O(1) |
| Observer Notify | O(n) | O(1) |
| Query Dedup | O(1) | O(1) |
| Mutation Lookup | O(1) | O(1) |