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:


📥 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.


🔗 Related Documentation