Deployment Architecture

Technical architecture for platform deployment with dynamic route registration

🚀 Deployment Architecture

Overview

The Modular Universe deployment system enables one-click deployment of platforms with dynamic route registration at runtime, using the same pattern as gograb-admin.


Architecture Diagram

graph TD
    A[Canvas Builder] --> B[Deploy Button Click]
    B --> C[PlatformDeploymentService]
    C --> D[Convert Canvas to Modules]
    D --> E[Generate Angular Routes]
    E --> F[Register Routes with Router]
    F --> G[Save Deployment Info]
    G --> H[Show Success Modal]
    H --> I[Platform Accessible]
    
    J[User Visits Platform] --> K[Angular Router]
    K --> L[Load Component]
    L --> M[PageModuleRenderer]
    M --> N[Load Page Content]
    N --> O[Render Widgets]
    O --> P[Execute Flows]

Key Components

1. PlatformDeploymentService

Location: libs/libs-structural/modular-universe/src/lib/universe-canvas-v3/services/platform-deployment.service.ts

Responsibilities:

Key Method:

async deployPlatform(universeId: string, modules: UniversalModule[]): Promise<DeploymentResult> {
  // 1. Generate routes from module tree
  const routes = this.generateRoutes(modules);

  // 2. Create platform route
  const platformRoute: Route = {
    path: `platform/${universeId}`,
    children: routes,
    data: { platformId: universeId, dynamicPlatform: true }
  };

  // 3. Register with Angular Router
  const currentConfig = [...this.router.config];
  currentConfig.push(platformRoute);
  this.router.resetConfig(currentConfig);

  // 4. Save deployment info
  await this.storage.set(`deployment_${universeId}`, {
    universeId,
    deployedAt: new Date().toISOString(),
    routes: routes.map(r => r.path),
    moduleCount: modules.length,
    basePath: `/platform/${universeId}`
  });

  return {
    success: true,
    basePath: `/platform/${universeId}`,
    routes: routes.map(r => r.path || '')
  };
}

2. PlatformStateService

Location: libs/libs-structural/modular-universe/src/lib/universe-canvas-v3/services/platform-state.service.ts

Responsibilities:

Flash Persistence:

// Current platform (30-day TTL)
readonly currentPlatform = autoFlashState('current-platform', null, {
  database: 'modular-universe',
  collection: 'platforms',
  priority: 'critical',
  ttl: 30 * 24 * 60 * 60 * 1000
});

// Deployment history (90-day TTL)
readonly deployedPlatforms = autoFlashState('deployed-platforms', [], {
  database: 'modular-universe',
  collection: 'deployments',
  priority: 'high',
  ttl: 90 * 24 * 60 * 60 * 1000
});

Route Generation

Module Tree to Routes

Input: Module tree from canvas

[
  {
    id: 'galaxy_shop',
    name: 'Shop',
    type: 'galaxy',
    route: '/shop',
    children: [
      {
        id: 'page_products',
        name: 'Products',
        type: 'page-module',
        route: '/shop/products'
      },
      {
        id: 'page_cart',
        name: 'Cart',
        type: 'page-module',
        route: '/shop/cart'
      }
    ]
  }
]

Output: Angular Routes

[
  {
    path: 'shop',
    loadComponent: () => import('...').then(m => m.PageModuleRendererComponent),
    data: { moduleId: 'galaxy_shop', moduleType: 'galaxy' },
    children: [
      {
        path: 'products',
        loadComponent: () => import('...').then(m => m.PageModuleRendererComponent),
        data: { moduleId: 'page_products', moduleType: 'page-module' }
      },
      {
        path: 'cart',
        loadComponent: () => import('...').then(m => m.PageModuleRendererComponent),
        data: { moduleId: 'page_cart', moduleType: 'page-module' }
      }
    ]
  }
]

Final Platform Route:

{
  path: 'platform/universe_123',
  children: [...generatedRoutes],
  data: { platformId: 'universe_123', dynamicPlatform: true }
}

Accessible At: /platform/universe_123/shop/products


Component Loading Strategy

Route → Component Mapping

private async getModuleComponent(module: UniversalModule): Promise<Type<any>> {
  // For page-modules, load PageModuleRenderer
  if (module.type === 'page' || module.type === 'world' || module.type === 'page-module') {
    const { PageModuleRendererComponent } = await import('../components/page-module-renderer/page-module-renderer.component');
    return PageModuleRendererComponent;
  }

  // For custom modules (from modularity-lib), load deployed component
  if (module.config?.moduleId) {
    return this.loadDeployedModuleComponent(module.config.moduleId);
  }

  // Default: generic module renderer
  const { ModuleRendererComponent } = await import('../components/module-renderer/module-renderer.component');
  return ModuleRendererComponent;
}

Benefits:


Storage Integration

Deployment Info Storage

Key Pattern: deployment_{universeId}

Data Structure:

interface DeploymentInfo {
  universeId: string;
  deployedAt: string;
  routes: string[];
  moduleCount: number;
  basePath: string;
}

Auto-Sync:

Platform State Storage

Key Pattern: platform_{platformId}

Data Structure:

interface PlatformState {
  id: string;
  name: string;
  modules: UniversalModule[];
  createdAt: string;
  updatedAt: string;
  version: number;
}

Flash Persistence:


Deployment Flow

1. User Clicks "Deploy"

async deployPlatform() {
  // Get universe ID
  const universeId = this.universeId();
  
  // Convert canvas to modules
  const modules = this.canvasConverter.canvasToModules(this.rootSection(), universeId);
  
  // Deploy
  const result = await this.platformDeployment.deployPlatform(universeId, modules);
  
  // Show result
  if (result.success) {
    this.deploymentResult.set(result);
    this.showDeploymentModal.set(true);
  }
}

2. Route Generation

private generateRoutes(modules: UniversalModule[]): Routes {
  const routes: Routes = [];

  for (const module of modules) {
    if (module.route || module.slug) {
      const route: Route = {
        path: this.sanitizePath(module.route || module.slug!),
        loadComponent: () => this.getModuleComponent(module),
        data: {
          moduleId: module.id,
          moduleName: module.name,
          moduleType: module.type
        }
      };

      // Recursive children
      if (module.children?.length > 0) {
        route.children = this.generateRoutes(module.children);
      }

      routes.push(route);
    }
  }

  return routes;
}

3. Router Registration

// Get current config
const currentConfig = [...this.router.config];

// Add platform route
currentConfig.push(platformRoute);

// Reset router (this is the magic!)
this.router.resetConfig(currentConfig);

4. Storage Persistence

// Save deployment info
await this.storage.set(`deployment_${universeId}`, deploymentInfo);

// Save to platform state (flash-persisted)
await this.platformState.deployPlatform(platform, basePath);

5. Success Modal

// Show deployment modal with:
// - Base URL
// - All routes
// - Quick actions (Visit, Share, Copy)
this.showDeploymentModal.set(true);

Comparison with gograb-admin

Similarities

Both use dynamic route registration:

// gograb-admin pattern
const moduleRoutes = manifest.routes.map(convertModuleRoute);
const currentConfig = [...this.router.config];
currentConfig.push(...moduleRoutes);
this.router.resetConfig(currentConfig);

// modular-universe pattern (same!)
const platformRoute = { path: `platform/${id}`, children: routes };
const currentConfig = [...this.router.config];
currentConfig.push(platformRoute);
this.router.resetConfig(currentConfig);

Differences

Feature gograb-admin modular-universe
Scope Single module Entire platform
Routes Module routes Platform + nested routes
Source Module manifest Canvas module tree
Base Path /{moduleId} /platform/{universeId}
Component Module components PageModuleRenderer + custom

Undeployment

async undeployPlatform(universeId: string): Promise<boolean> {
  // Remove from router config
  const currentConfig = this.router.config.filter(
    route => route.path !== `platform/${universeId}`
  );
  this.router.resetConfig(currentConfig);

  // Remove from storage
  await this.storage.delete(`deployment_${universeId}`);

  // Update state
  this.platformState.undeployPlatform(universeId);

  return true;
}

Performance Considerations

Route Registration

Component Loading

Storage


Troubleshooting

Routes Not Registering

Symptoms: Platform deployed but routes don't work

Causes:

  1. Router config not reset
  2. Route paths invalid
  3. Components not loading

Fix:

// Check router config
console.log(this.router.config);

// Verify platform route exists
const platformRoute = this.router.config.find(r => r.path === `platform/${universeId}`);
console.log('Platform route:', platformRoute);

Components Not Loading

Symptoms: Blank page or error on navigation

Causes:

  1. Component import failed
  2. Module not found
  3. Page content missing

Fix:

// Check component loading
const component = await this.getModuleComponent(module);
console.log('Component:', component);

// Verify page content
const page = await this.pagesStateService.pages().find(p => p.id === pageId);
console.log('Page:', page);

Deployment Info Not Persisting

Symptoms: Deployment info lost on refresh

Causes:

  1. Storage not initialized
  2. Flash persistence not working
  3. Key pattern incorrect

Fix:

// Check storage
const info = await this.storage.get(`deployment_${universeId}`);
console.log('Deployment info:', info);

// Check flash persistence
const platforms = this.platformState.deployedPlatforms();
console.log('Deployed platforms:', platforms);

Related Documentation


Last Updated: November 27, 2025 - 2:20 PM
Session ID: 11.25.2.9
Status: ✅ Complete and Production-Ready