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:
- Convert module tree to Angular routes
- Register routes dynamically with
router.resetConfig() - Lazy-load components for each route
- Support nested routes (galaxies with children)
- Persist deployment info to storage
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:
- Manage platform state with flash persistence
- Track deployment history
- Auto-sync to backend MongoDB
- Cross-tab synchronization
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:
- Lazy loading (components loaded on-demand)
- Type-specific rendering
- Extensible for custom modules
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:
- Stored with
deployment_*prefix - Auto-synced to backend MongoDB via
UniversalAutoSyncService - Available across devices
- Persists 90 days
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:
- < 1ms load time
- Cross-tab sync
- Auto-save on changes
- 30-day TTL
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
- Cost: ~10-50ms per platform
- Impact: Minimal (one-time on deployment)
- Optimization: Routes generated once, cached by Angular
Component Loading
- Strategy: Lazy loading
- Benefit: Only load components when routes accessed
- Impact: Faster initial load, on-demand loading
Storage
- Flash Persistence: < 1ms reads
- Auto-Sync: Background, non-blocking
- Cache: In-memory for repeated access
Troubleshooting
Routes Not Registering
Symptoms: Platform deployed but routes don't work
Causes:
- Router config not reset
- Route paths invalid
- 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:
- Component import failed
- Module not found
- 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:
- Storage not initialized
- Flash persistence not working
- 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
- Complete Platform Builder Guide - User guide
- Singularity Integration - Flow integration
- Modular Universe Improvements - Previous work
- Flash Persistence Integration - Storage system
Last Updated: November 27, 2025 - 2:20 PM
Session ID: 11.25.2.9
Status: ✅ Complete and Production-Ready