Page Module Architecture
Clean separation between platform structure (modules) and visual content (pages)
Page Module Architecture
🎯 Problem Statement
Previously, UniversalModule tried to do too much:
- Structure: routes, navigation, hierarchy, permissions
- Content: widgets, layout, SEO, responsive settings
This mixing caused confusion and made it hard to:
- Reuse page content in different platform contexts
- Understand what belongs where
- Maintain clean separation of concerns
✅ Solution: Clean Separation
The Two Domains
┌────────────────────────────────────┬────────────────────────────────────┐
│ UniversalModule │ Page (editor-web-lib) │
│ (Platform Structure) │ (Visual Content) │
├────────────────────────────────────┼────────────────────────────────────┤
│ WHERE things go: │ WHAT things look like: │
│ • route │ • widgets: Widget[] │
│ • navigation config │ • layout: ResponsiveLayout │
│ • permissions │ • meta: PageMeta (SEO) │
│ • module hierarchy │ • devicePreferences │
│ • behavior (flows) │ • draftInfo, templates │
│ • slots for customization │ • status (draft/published) │
└────────────────────────────────────┴────────────────────────────────────┘
The Bridge: page-module Type
// UniversalModule with type: 'page-module'
const pageModule: UniversalModule = {
id: 'products-page',
name: 'Products Page',
slug: 'products',
type: 'page-module', // 🆕 The new type!
route: '/products',
// PageModuleConfig bridges to the actual Page content
config: {
pageId: 'page_12345', // Reference to Page from editor-web-lib
navigation: {
isNavigable: true,
navLabel: 'Products',
navIcon: 'fa-shopping-cart',
navOrder: 2,
showBreadcrumbs: true
},
slots: [
{ id: 'breadcrumb', position: 'before-content', isVisible: true }
],
flowTriggers: {
'onLoad': 'flow_load_products'
}
} as PageModuleConfig
};
📦 Key Interfaces
PageModuleConfig
interface PageModuleConfig extends ModuleConfig {
// Content reference
pageId: string; // Reference to Page from editor-web-lib
// Routing
routePath?: string; // URL route path
routeParams?: Record<string, string>;
routeGuards?: string[];
redirectTo?: string;
// Navigation
navigation: PageNavigationConfig;
// Customization
slots?: PageSlot[]; // Add modules around page content
// Behavior
flowTriggers?: Record<string, string>;
flowParams?: Record<string, Record<string, string>>;
// Layout override
layoutOverride?: 'default' | 'fullscreen' | 'sidebar' | 'modal';
// Permissions
requiredPermissions?: string[];
}
PageNavigationConfig
interface PageNavigationConfig {
isNavigable: boolean; // Show in navigation menus
navLabel?: string; // Label (defaults to page title)
navIcon?: string; // FontAwesome icon
navOrder?: number; // Order in menu
navGroup?: string; // Group/section
showBreadcrumbs?: boolean;
}
PageSlot
interface PageSlot {
id: string;
position: 'before-content' | 'after-content' | 'header' | 'footer' | 'sidebar-left' | 'sidebar-right';
moduleId?: string; // Module to render in this slot
isVisible: boolean;
className?: string;
}
🔄 Migration Guide
Before (Deprecated)
// ❌ OLD WAY - widgets/layout directly on module
const oldModule: UniversalModule = {
type: 'page',
widgets: [...], // Mixed content with structure!
layout: {...}, // Mixed content with structure!
route: '/products'
};
After (New Way)
// ✅ NEW WAY - Clean separation
// 1. Page (in editor-web-lib) - content only
const page: Page = {
id: 'page_12345',
title: 'Products',
widgets: [...],
layout: {...},
meta: { title: 'Products | My Store', ... }
};
// 2. PageModule (in modular-universe) - structure only
const pageModule: UniversalModule = {
type: 'page-module',
route: '/products',
config: {
pageId: 'page_12345',
navigation: { isNavigable: true, navLabel: 'Products' }
} as PageModuleConfig
};
🎯 Benefits
- Reusability: Same page content can be used in multiple platform contexts
- Clarity: Clear what belongs where (structure vs content)
- Flexibility: Change navigation/routing without touching page content
- Customization: Add slots for breadcrumbs, headers, sidebars without modifying page
- AI-Friendly: Clear boundaries make AI training easier
📝 Deprecated Types
type: 'page' - DEPRECATED
The old page type mixed structure with content. Use page-module instead.
// ❌ Deprecated
type: 'page'
// ✅ Use instead
type: 'page-module'
widgets, layout, sections on UniversalModule - DEPRECATED
These fields on UniversalModule are deprecated. Content should be in the Page model.
// ❌ Deprecated - don't use these on UniversalModule
widgets?: any[];
layout?: any;
sections?: any[];
// ✅ Use PageModuleConfig.pageId to reference Page content
config: {
pageId: 'page_12345'
} as PageModuleConfig
🔗 Related Files
libs/libs-foundational/core-models/src/lib/types/modules/universal-module.types.ts- UniversalModule, PageModuleConfiglibs/libs-foundational/core-models/src/lib/types/modules/modules.types.ts- ModuleType with 'page-module'libs/libs-foundational/core-models/src/lib/types/page.types.ts- Page (visual content)
Last Updated: November 26, 2025 Author: Architecture Team