THE ONE WAY
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
THE ONE WAY - Universe Module Architecture
Session 40.2 - January 2026
One Format. One Flow. One Source of Truth.
🎯 The Data Model
// THE ONE FORMAT: UniversalModule[]
// Used by ALL tools: vibe-universe, canvas, architect, modular-universe
interface UniversalModule {
id: string;
name: string;
slug: string;
type: "universe-root" | "galaxy" | "world" | "page-module" | "layout" | "navigation";
// ⭐ THE KEY: Optional reference to code module
moduleRef?: string; // Points to space_modules._id or /modules/business/{name}
route?: string;
config: Record<string, unknown>;
children?: UniversalModule[];
// ... other fields (ai, layout, styles, etc.)
}
// Note: 'universe-root' naming chosen in Session 46.3 to avoid conflict
// with 'Universe' (the MongoDB document name). See FINAL-COSMIC-ARCHITECTURE.md
📊 Storage Architecture
MongoDB: space_{spaceId}/
├─ universes/{universeId}
│ └─ structure: UniverseStructureNode[] ← Hierarchy (what the ONE WAY saves to)
│
├─ modules/{moduleId} ← Reusable code modules (optional)
│ └─ _id: "restaurant-platform-menu-item-management"
│ manifest: { publicActions, embeddableComponents, ... }
│ modulePath: "/modules/business/restaurant-platform-menu-item-management"
│
├─ pages/{pageId} ← Page content (widgets, layout)
└─ widgets/{widgetId} ← Widget definitions
🔄 THE ONE WAY TO SAVE
// ALL tools use this SAME method:
await universeModuleService.replaceTree(universeId, modules);
// Internally, this ALWAYS:
// 1. Converts UniversalModule[] to structure nodes
// 2. Saves NEW modules to space_modules (if no moduleRef)
// 3. Updates Universe.structure with hierarchy
// 4. Broadcasts CrossToolSyncService events
What happens with moduleRef:
- Has moduleRef → Structure references existing code module (reusable)
- No moduleRef → Structure creates new structural module (layout, navigation)
📥 THE ONE WAY TO LOAD
// ALL tools use this SAME method:
const modules = await universeModuleService.load(universeId);
// Returns: Hydrated UniversalModule[] tree
// - Loads from Universe.structure
// - Optionally hydrates from space_modules if moduleRef exists
🏗️ Module Types & When to Use moduleRef
| Module Type | Has moduleRef? | Example |
|---|---|---|
| Business Module (world) | ✅ YES | Menu Management, Order Management |
| Layout | ❌ NO | Main Layout, Split Layout |
| Navigation | ❌ NO | Header, Sidebar, Footer |
| Galaxy | ⚠️ DEPENDS | Container galaxy = NO, Business portal = YES |
| Page Module | ⚠️ DEPENDS | Generic page = NO, CMS page with code = YES |
🤖 AI Generation Flow
Vibe Universe Generator:
// 1. AI generates business modules → /modules/business/
await backendAiApi.generateModule({
name: "menu-item-management",
prompt: "Manage restaurant menu items with CRUD..."
});
// Creates: /modules/business/restaurant-platform-menu-item-management/
// ├─ components/, actions/, models/
// └─ menu-item-management.manifest.ts
// 2. Register in space_modules
const codeModule = await backendModuleApi.createSpaceModule(spaceId, {
_id: "restaurant-platform-menu-item-management",
name: "Menu Item Management",
manifest: { publicActions, embeddableComponents, ... }
});
// 3. Create UniversalModule with moduleRef
const module: UniversalModule = {
id: generateId(),
name: "Menu Management",
type: "world",
route: "/menu",
moduleRef: codeModule._id, // ⭐ THE CONNECTION
config: {}
};
// 4. Save to Universe
await universeModuleService.replaceTree(universeId, [module]);
Vibe Code (Structural):
// Generates layouts and navigation (NO moduleRef)
const structure: UniversalModule[] = [
{
id: generateId(),
name: "Main Layout",
type: "layout",
moduleId: "layout-container", // Renderer
moduleRef: undefined, // ❌ Structural, no code module
config: {},
children: [
{
id: generateId(),
name: "Header",
type: "navigation",
moduleId: "top-nav",
moduleRef: undefined, // ❌ Structural
config: {}
}
]
}
];
await universeModuleService.replaceTree(universeId, structure);
✅ Verifying THE ONE WAY
Check 1: No Dual Paths
# Should find ZERO results:
grep -r "Universe.modules\[\]" libs/
grep -r "persistTree(" libs/ --exclude="*universe-module.service.ts"
grep -r "useFlatArchitecture" libs/
Check 2: moduleRef Usage
# Business modules SHOULD have moduleRef:
grep -r "moduleRef:" libs/libs-structural/vibe-universe/
# Structural modules should NOT:
grep -r "type: 'layout'" libs/libs-space/universe-canvas/
grep -r "type: 'navigation'" libs/libs-space/universe-canvas/
Check 3: Single Save Path
# All mutations should call persistTreeToFlatArchitecture:
grep "persistTreeToFlatArchitecture" libs/libs-foundational/core-angular/src/lib/services/universe-module.service.ts
# Should see 4 calls: upsertModule, deleteModule, moveModule, updateModule
🚀 Benefits of THE ONE WAY
| Before (Dual Path) | After (One Way) |
|---|---|
| 2 save methods | 1 save method |
| 2 load paths | 1 load path |
| Legacy + flat options | Flat only |
| ~500 lines of conversion | ~20 lines |
| Confusing comments everywhere | Clear, simple docs |
| "Which path do I use?" | No question - use THE path |
📝 Summary
THE ONE FORMAT: UniversalModule[]
THE ONE SAVE: replaceTree() → persistTreeToFlatArchitecture()
THE ONE LOAD: load() → loadFlatArchitecture()
THE ONE CONNECTION: moduleRef links to code modules
No conversions. No dual paths. No confusion.