PERSISTENCE ARCHITECTURE
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
Singularity: Systematic Persistence Architecture
Date: October 8, 2025
Status: ✅ Fixed and Documented
🎯 The Systematic Approach
This document explains how we systematically fixed persistence issues in the Singularity logic builder.
🚨 The Problem
Connections were not persisting across page refreshes, despite blocks persisting correctly. This revealed a systematic architectural problem:
Root Cause: Duplicate State Management
// ❌ BAD: Two sources of truth
blockConnections: Array<LogicConnection> = []; // Local array
canvasConnections = signal<LogicConnection[]>([]); // Flash Persistence
// Template was using the wrong one!
@for (connection of blockConnections; track ...) { } // ❌ Local only!
Result: Connections were stored locally but never persisted to Flash Persistence.
✅ The Systematic Fix
Principle: Single Source of Truth
All persistent data MUST use Flash Persistence via SingularityService
// ✅ GOOD: Single source of truth
// NO local arrays, only Flash Persistence signals
canvasBlocks = signal<LogicBlock[]>([]); // From Flash Persistence
canvasConnections = signal<LogicConnection[]>([]); // From Flash Persistence
Pattern Applied
// 1️⃣ Create/Update Data
const connection: LogicConnection = {
id: generateId(),
sourceBlock: source.id,
targetBlock: target.id,
// ... other fields
createdAt: new Date(),
updatedAt: new Date()
};
// 2️⃣ Save to Flash Persistence (Single Source of Truth)
this.singularityService.addConnection(connection);
// 3️⃣ UI updates automatically via signal reactivity
// Template: @for (connection of canvasConnections(); track ...)
📋 Architecture Checklist
✅ Data Layer (Flash Persistence)
- Blocks:
singularityService.blocks() - Connections:
singularityService.connections() - Templates:
singularityService.blockTemplates() - Categories:
singularityService.blockCategories() - UI State: Various
singularityServicesignals
✅ Component Layer (Logic Builder)
- Read: Use
singularityServicesignals - Write: Call
singularityServicemethods - NO Local State: No duplicate arrays or objects
✅ Template Layer (HTML)
- Blocks:
@for (block of canvasBlocks(); track ...) - Connections:
@for (connection of canvasConnections(); track ...)
🔧 Files Modified
1. logic-builder.component.ts
Before:
❌ blockConnections: Array<LogicConnection> = []; // Local only
❌ this.blockConnections.push(connection); // Never persists
After:
✅ // No local array - use Flash Persistence
✅ this.singularityService.addConnection(connection); // Persists automatically
2. logic-builder.component.html
Before:
❌ @for (connection of blockConnections; track ...) { }
After:
✅ @for (connection of canvasConnections(); track ...) { }
3. updateConnectionCoordinates()
Before:
❌ this.blockConnections.forEach(connection => {
❌ connection.x1 = newX; // Mutates local array
❌ });
After:
✅ const connections = this.canvasConnections();
✅ const updatedConnections = connections.map(conn => ({
✅ ...conn,
✅ x1: newX // Immutable update
✅ }));
✅ this.singularityService.setConnections(updatedConnections);
🎯 The Systematic Pattern
Rule 1: Single Source of Truth
// ✅ ALWAYS use Flash Persistence
readonly blocks = computed(() => this.singularityService.blocks());
// ❌ NEVER create local copies
private myBlocks: LogicBlock[] = []; // DON'T DO THIS!
Rule 2: Immutable Updates
// ✅ GOOD: Immutable update
this.singularityService.setBlocks([
...currentBlocks,
newBlock
]);
// ❌ BAD: Mutating array
this.myBlocks.push(newBlock);
Rule 3: Automatic Persistence
// ✅ Flash Persistence handles everything
this.singularityService.addConnection(connection);
// Automatically:
// - Saves to IndexedDB
// - Syncs to Firebase
// - Updates all subscribers
// - Triggers change detection
🧪 Testing Checklist
Before Code Review:
- 1. Create blocks → Refresh → Blocks persist ✅
- 2. Create connections → Refresh → Connections persist ✅
- 3. Move blocks → Refresh → Positions persist ✅
- 4. Delete blocks → Refresh → Deletions persist ✅
- 5. Delete connections → Refresh → Deletions persist ✅
Verification:
// Check console logs:
✅ "💾 Flash Persistence: Persisted flash:singularity:blocks"
✅ "💾 Flash Persistence: Persisted flash:singularity:connections"
✅ "🎯 flash:singularity:blocks loaded with persisted value"
✅ "🎯 flash:singularity:connections loaded with persisted value"
🚀 Benefits of This Architecture
1. Automatic Persistence
- No manual save/load code
- Works across all components
- Zero configuration
2. Reactive UI
- Signals trigger change detection automatically
- No
markForCheck()needed - Smooth, responsive UI
3. Data Integrity
- Single source of truth prevents inconsistencies
- Immutable updates prevent bugs
- Type safety with TypeScript
4. Scalability
- Easy to add new persistent state
- Pattern is consistent everywhere
- Team can follow the same approach
📚 Related Documentation
- Flash Persistence Common Issues
- Flash Persistence Production Checklist
- Flash Persistence Ultra Simple Guide
💡 Key Takeaways
- Never create local copies of persistent data
- Always use SingularityService for state management
- Template reads from
signal()directly - Updates go through service methods
- Flash Persistence handles the rest
✅ Verification Complete
All persistent data now follows this systematic architecture:
- ✅ Blocks persist
- ✅ Connections persist
- ✅ Positions persist
- ✅ UI state persists
- ✅ Undo/redo history persists
No more manual save/load code!
No more duplicate state!
No more persistence bugs!
Version: 1.0.0
Status: ✅ Production Ready