Overview

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

Flash Persistence - Overview

Flash Persistence is an auto-persistent signal system that eliminates the boilerplate of manual state management and storage synchronization.


Table of Contents

  1. What is Flash Persistence?
  2. Architecture
  3. Core Concepts
  4. How It Works
  5. Performance
  6. Comparison

What is Flash Persistence?

Flash Persistence combines Angular signals with intelligent storage to create persistent state that:

The Problem It Solves

Traditional Approach:

// ❌ Manual storage management
class PreferencesService {
  private preferences = signal({ theme: 'dark' });
  
  async loadPreferences() {
    const stored = await storage.get('preferences');
    if (stored) this.preferences.set(stored);
  }
  
  async savePreferences(prefs: Preferences) {
    this.preferences.set(prefs);
    await storage.put('preferences', prefs);
  }
}

// ❌ Must call manually everywhere
await prefsService.loadPreferences(); // On init
await prefsService.savePreferences(newPrefs); // On change

Flash Persistence:

// ✅ Automatic persistence
const preferences = autoFlashState({
  key: 'preferences',
  initialValue: { theme: 'dark' }
});

// ✅ Just use it - persistence is automatic
preferences.set({ theme: 'light' }); // Saved automatically!
const theme = preferences().theme; // Instant read from cache

Architecture

High-Level Overview

┌─────────────────────────────────────────────────────────────┐
│                    Angular Component                         │
│                                                              │
│  const userPrefs = autoFlashState({...});                   │
│  const theme = userPrefs().theme;  ← 0.3ms read            │
│  userPrefs.set({...});             ← Triggers persistence   │
└──────────────────┬──────────────────────────────────────────┘
                   │
┌──────────────────▼───────────────────────────────────────────┐
│              FlashStateHydratorService                        │
│  • Pre-loads values on app init                              │
│  • Manages persistence queue                                 │
│  • Debounces writes (300ms default)                          │
│  • Provides instant cache access                             │
└──────────────────┬───────────────────────────────────────────┘
                   │
┌──────────────────▼───────────────────────────────────────────┐
│                   GgStorage Engine                            │
│  • RAM Cache (0.3ms reads)                                   │
│  • Multi-adapter selection                                   │
│  • Intelligent redundancy                                    │
└──────────────────┬───────────────────────────────────────────┘
                   │
        ┌──────────┼──────────┐
        │          │          │
┌───────▼────┐ ┌──▼──────┐ ┌─▼─────────┐
│   OPFS     │ │  Dexie  │ │localStorage│
│ (file-like)│ │(indexed)│ │  (simple) │
└────────────┘ └─────────┘ └───────────┘
        │          │          │
        └──────────┼──────────┘
                   │ (optional)
        ┌──────────▼──────────┐
        │   MongoDB Sync      │
        │  (background)       │
        └─────────────────────┘

Component Interaction

// 1. Signal Creation
const state = autoFlashState({
  key: 'user-prefs',
  initialValue: { theme: 'dark' }
});

// 2. Value is immediately available (pre-loaded or initial)
console.log(state()); // { theme: 'dark' }

// 3. Read triggers cache lookup (0.3ms)
const theme = state().theme;

// 4. Write triggers persistence pipeline
state.set({ theme: 'light' });
  ↓
FlashStateHydrator (debounce 300ms)
  ↓
GgStorage (intelligent adapter)
  ↓
Storage Backend (OPFS/Dexie/localStorage)
  ↓
(Optional) MongoDB Sync

Core Concepts

1. Auto-Persistent Signals

Flash Persistence signals are regular Angular signals with persistence built-in:

// It's still a signal!
const counter = autoFlashState({
  key: 'counter',
  initialValue: 0
});

// All signal features work
console.log(counter()); // Read
counter.set(5); // Write
counter.update(n => n + 1); // Update

// But also persisted!
// Refresh page → value is still 6

2. Hierarchical Keys

Keys can be organized hierarchically for better organization:

const userPrefs = autoFlashState({
  key: 'preferences',
  database: 'app-data',
  collection: 'user-settings',
  namespace: 'profile'
  // Full key: app-data:profile:user-settings:preferences
});

Benefits:

3. Intelligent Storage

The system automatically selects the best storage adapter:

// System decides based on:
// - Data size
// - Access patterns
// - Data type
// - Priority level

const smallData = autoFlashState({
  key: 'theme',
  initialValue: 'dark'
}); // → localStorage (fast, simple)

const largeData = autoFlashState({
  key: 'large-dataset',
  initialValue: hugeArray
}); // → OPFS (best for large data)

const criticalData = autoFlashState({
  key: 'auth-token',
  initialValue: null,
  priority: 'critical'
}); // → Multi-adapter redundancy

4. Optional Sync

Sync is opt-in and configurable:

// No sync (offline-only)
const localState = autoFlashState({
  key: 'draft',
  initialValue: ''
});

// MongoDB sync with priority
const syncedState = autoFlashState({
  key: 'shared-doc',
  database: 'collaboration',
  collection: 'documents',
  initialValue: '',
  priority: 'high' // Sync every 5 minutes
});

// Real-time sync
const realtimeState = autoFlashState({
  key: 'chat-messages',
  database: 'chat',
  collection: 'messages',
  initialValue: [],
  priority: 'critical' // Immediate sync
});

How It Works

Initialization Flow

// 1. App starts → initialization runs
provideFlashPersistence() // in app.config.ts
  ↓
FlashStateHydratorService.preload()
  ↓
// Pre-loads all discovered states from storage
AUTO_DISCOVERED_STATES.forEach(state => {
  const value = await storage.get(state.fullKey);
  if (value) cache.set(state.fullKey, value);
});

Read Flow

// User reads value
const value = userPrefs();
  ↓
// Signal getter called
signal.value
  ↓
// Value already in memory (pre-loaded)
// Return time: ~0.3ms (RAM access)

Write Flow

// User updates value
userPrefs.set({ theme: 'light' });
  ↓
// Signal setter called
signal.set(newValue)
  ↓
// UI updates immediately (signal reactivity)
  ↓
// Hydrator notified
FlashStateHydrator.onStateChange()
  ↓
// Debounce 300ms (avoid excessive writes)
  ↓
// Persist to storage
await storage.put(key, value)
  ↓
// (Optional) Queue for MongoDB sync
if (priority) syncEngine.enqueue({
  collection,
  data: value,
  priority
});

Performance

Benchmarks

Operation Time Notes
Read (cached) 0.3ms RAM cache hit
Read (storage) 1-2ms localStorage/Dexie
Write (local) 1-5ms Debounced, batched
Sync (background) 50-200ms Non-blocking
Real-time update 10-50ms Change Streams

Memory Efficiency

Network Efficiency


Comparison

vs. NGXS

Feature Flash Persistence NGXS
Setup One function call Store, actions, state, selectors
Boilerplate Zero High (actions, reducers)
Persistence Automatic Manual plugin configuration
TypeScript Full inference Requires decorators
Performance 0.3ms cache Depends on implementation
Sync Built-in (MongoDB) External solution needed
Learning Curve Minimal (signals) Moderate (Redux pattern)

Migration:

// NGXS
@State({
  name: 'preferences',
  defaults: { theme: 'dark' }
})
export class PreferencesState {
  @Action(SetTheme)
  setTheme(ctx: StateContext<Preferences>, action: SetTheme) {
    ctx.patchState({ theme: action.theme });
  }
}

// Flash Persistence
const preferences = autoFlashState({
  key: 'preferences',
  initialValue: { theme: 'dark' }
});
preferences.update(p => ({ ...p, theme: 'light' }));

vs. RxJS + LocalStorage

Feature Flash Persistence RxJS + Storage
Reactivity Signals (built-in) BehaviorSubject
Persistence Automatic Manual save()
Loading Pre-loaded Manual load()
Caching Built-in (0.3ms) Must implement
Sync Built-in Must implement
Memory Leaks Auto-managed Manual unsubscribe

Comparison:

// RxJS + LocalStorage
class PrefsService {
  private prefs$ = new BehaviorSubject<Preferences>(null);
  
  async load() {
    const stored = localStorage.getItem('prefs');
    if (stored) this.prefs$.next(JSON.parse(stored));
  }
  
  async save(prefs: Preferences) {
    this.prefs$.next(prefs);
    localStorage.setItem('prefs', JSON.stringify(prefs));
  }
  
  ngOnDestroy() {
    this.prefs$.complete(); // Must remember!
  }
}

// Flash Persistence
const prefs = autoFlashState({
  key: 'prefs',
  initialValue: null
});
// That's it! No load(), save(), or cleanup needed

vs. IndexedDB Direct

Feature Flash Persistence Direct IndexedDB
API Simple signals Complex async API
Caching Automatic Manual
Reactivity Built-in Must implement
Multi-adapter Automatic Single storage
Sync Built-in Must implement
Error Handling Automatic Manual try/catch everywhere

Next Steps


Ready to eliminate state management boilerplate? → Quick Start