Ai System Config Design
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
AI System Configuration - Design Document
Overview
Este documento describe el diseño de AISystemConfiguration que reemplaza a AppUiConfiguration como la configuración principal del sistema GoGrab Space. El objetivo es crear un PoC de un sistema de módulos dinámicos que use configuración generada por IA.
Nota: Fase de desarrollo - sin retrocompatibilidad. Enfoque directo y simplificado.
Decisiones de Diseño
| Aspecto | Decisión | Justificación |
|---|---|---|
| Granularidad | Híbrido (sistema base + páginas dinámicas) | Flexibilidad sin complejidad excesiva |
| Escenario PoC | Sistema de inventario para restaurante | Caso concreto y representativo |
| Fuente de Config | Prompt natural (IA interpreta texto) | UX más natural para usuarios |
| Runtime | Semi-dinámico (config base + ajustes usuario) | Balance entre flexibilidad y rendimiento |
| Complejidad | Simplificado para PoC (sin reglas condicionales) | Enfocarse en flujo principal |
| Estrategia | Reemplazar directamente (sin retrocompatibilidad) | Código limpio, sin legacy |
Arquitectura
Principio de Reemplazo Directo
┌─────────────────────────────────────────────────────────────────────────┐
│ REEMPLAZO DIRECTO - SIN RETROCOMPATIBILIDAD │
│ │
│ ELIMINAR NUEVO │
│ ════════ ═════ │
│ AppUiConfiguration ──► AISystemConfiguration │
│ ModuleConfigurationService ──► AppConfigService (unificado) │
│ Endpoint modules/config ──► Endpoint /api/config/ui │
│ │
│ AISystemConfiguration: │
│ ├─ navigation (de AppUiConfiguration) │
│ ├─ dashboard (de AppUiConfiguration) │
│ ├─ topNav (de AppUiConfiguration) │
│ ├─ branding (de AppUiConfiguration) │
│ ├─ generatedAt (de AppUiConfiguration) │
│ ├─ version (de AppUiConfiguration) │
│ │ │
│ │ + NUEVAS CAPACIDADES: │
│ ├─ scenario (metadata del caso de uso) │
│ ├─ generatedFrom (info de generación IA) │
│ ├─ modules (activación de módulos) ◄── REEMPLAZA ModuleConfig │
│ ├─ pages (páginas dinámicas) │
│ └─ theme (customización de tema) │
└─────────────────────────────────────────────────────────────────────────┘
Servicios a Eliminar/Modificar
| Servicio | Acción | Razón |
|---|---|---|
ModuleConfigurationService |
ELIMINAR | Unificado en AppConfigService |
AppConfigService |
MODIFICAR | Agregar signals para módulos, páginas, etc. |
BrowserBootstrapperService |
MODIFICAR | Usar AppConfigService.enabledModules() |
| Mock API legacy endpoint | ELIMINAR | Todo en /api/config/ui |
Integración con Sistema Existente
┌─────────────────────────────────────────────────────────────────────────┐
│ INTEGRACIÓN CON BIBLIOTECAS CORE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ @libs-core/module-config │
│ ├── AppUiConfiguration ◄────── ELIMINAR (reemplazado) │
│ ├── AISystemConfiguration ◄─── NUEVO (reemplaza todo) │
│ ├── WidgetPlacement ◄────────── WidgetInstanceConfig EXTIENDE │
│ └── ResolvedNavigationConfig (se reutiliza directamente) │
│ │
│ @libs-core/module-base │
│ ├── ModuleConfigurationService ◄── ELIMINAR │
│ ├── ModuleManifest (widgets vienen de aquí) │
│ └── ModuleEmbeddableComponent (definición de widgets disponibles) │
│ │
│ @libs-core/data-binding │
│ └── DataBinding ◄────────────── WidgetInstanceConfig.bindings USA │
│ │
│ @libs-core/module-integrator │
│ └── BrowserBootstrapperService (usa AppConfigService.enabledModules) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Schema de Interfaces
AISystemConfiguration (Principal)
import type { DataBinding } from '@libs-core/data-binding';
import type { BrandingConfig } from '@libs-core/shared-models';
import type { ResolvedNavigationConfig } from './resolved-navigation-config';
import type { ResolvedDashboardConfig } from './resolved-dashboard-config';
import type { ResolvedTopNavConfig } from './resolved-topnav-config';
import type { WidgetPlacement } from './tenant-customization';
/**
* Configuración completa del sistema.
* Reemplaza AppUiConfiguration y ModuleConfigurationRegistry.
*/
export interface AISystemConfiguration {
// ═══════════════════════════════════════════════════════════════════
// Propiedades de configuración UI (antes en AppUiConfiguration)
// ═══════════════════════════════════════════════════════════════════
/** Configuración de navegación completa con grupos y jerarquías */
navigation: ResolvedNavigationConfig;
/** Configuración de dashboard con widgets y layout */
dashboard: ResolvedDashboardConfig;
/** Configuración de TopNav con branding */
topNav: ResolvedTopNavConfig;
/** Branding del tenant (logo, colores, nombre empresa) */
branding: BrandingConfig;
/** Timestamp de generación de esta configuración */
generatedAt: Date;
/** Versión del schema de configuración */
version: string;
// ═══════════════════════════════════════════════════════════════════
// Nuevas propiedades para configuración IA
// ═══════════════════════════════════════════════════════════════════
/** Metadata del escenario/caso de uso */
scenario: ScenarioConfig;
/** Información sobre cómo se generó esta configuración */
generatedFrom: AIGenerationMetadata;
/** Módulos activos y su configuración (reemplaza ModuleConfigurationRegistry) */
modules: ModuleActivationConfig[];
/** Páginas dinámicas compuestas con widgets */
pages: PageConfig[];
/** Customización de tema (opcional) */
theme?: ThemeCustomization;
}
ScenarioConfig
/**
* Metadata del escenario/caso de uso.
*/
export interface ScenarioConfig {
id: string;
name: string;
description: string;
industry: string;
tags: string[];
}
AIGenerationMetadata
/**
* Información sobre cómo se generó la configuración.
*/
export interface AIGenerationMetadata {
source: 'ai' | 'manual' | 'template';
originalPrompt?: string;
generatedAt: Date;
}
ModuleActivationConfig
/**
* Configuración de activación de un módulo.
* REEMPLAZA ModuleConfigurationRegistry del sistema anterior.
*/
export interface ModuleActivationConfig {
/** ID del módulo (debe existir en registry) */
moduleId: string;
/** Si el módulo está activo */
enabled: boolean;
/** Configuración simple key-value */
config?: Record<string, unknown>;
}
PageConfig
/**
* Configuración de una página dinámica.
*/
export interface PageConfig {
id: string;
route: string;
title: string;
icon?: string;
layout: PageLayoutConfig;
widgets: WidgetInstanceConfig[];
permissions?: string[];
}
export interface PageLayoutConfig {
type: 'grid' | 'flex' | 'single';
columns?: number;
gap?: string;
}
WidgetInstanceConfig
/**
* Instancia de widget con data bindings.
* Extiende WidgetPlacement con integración a data-binding.
*/
export interface WidgetInstanceConfig extends WidgetPlacement {
bindings?: DataBinding[];
}
ThemeCustomization
/**
* Customización básica de tema.
*/
export interface ThemeCustomization {
baseTheme: 'light' | 'dark';
colors?: {
primary?: string;
secondary?: string;
accent?: string;
};
}
AppConfigService - Signals Unificados
// libs/core/module-config/src/lib/services/app-config.service.ts
@Injectable({ providedIn: 'root' })
export class AppConfigService {
// Signal principal
private configSignal = signal<AISystemConfiguration | null>(null);
readonly configuration = this.configSignal.asReadonly();
// ═══════════════════════════════════════════════════════════════════
// Signals existentes (UI config)
// ═══════════════════════════════════════════════════════════════════
readonly navigationConfig = computed(() => this.configuration()?.navigation ?? null);
readonly dashboardConfig = computed(() => this.configuration()?.dashboard ?? null);
readonly topNavConfig = computed(() => this.configuration()?.topNav ?? null);
readonly availableWidgets = computed(() => this.configuration()?.dashboard?.availableWidgets ?? []);
// ═══════════════════════════════════════════════════════════════════
// NUEVOS Signals para módulos (REEMPLAZA ModuleConfigurationService)
// ═══════════════════════════════════════════════════════════════════
/** Todos los módulos configurados */
readonly modulesConfig = computed(() => this.configuration()?.modules ?? []);
/** Solo módulos habilitados */
readonly enabledModules = computed(() =>
this.modulesConfig().filter(m => m.enabled)
);
/** IDs de módulos habilitados */
readonly enabledModuleIds = computed(() =>
this.enabledModules().map(m => m.moduleId)
);
/** Verificar si módulo está habilitado */
isModuleEnabled(moduleId: string): boolean {
return this.enabledModuleIds().includes(moduleId);
}
/** Obtener config de módulo específico */
getModuleConfig(moduleId: string): ModuleActivationConfig | undefined {
return this.modulesConfig().find(m => m.moduleId === moduleId);
}
// ═══════════════════════════════════════════════════════════════════
// NUEVOS Signals para páginas dinámicas
// ═══════════════════════════════════════════════════════════════════
readonly pagesConfig = computed(() => this.configuration()?.pages ?? []);
readonly scenarioConfig = computed(() => this.configuration()?.scenario ?? null);
readonly themeConfig = computed(() => this.configuration()?.theme ?? null);
readonly generatedFromInfo = computed(() => this.configuration()?.generatedFrom ?? null);
}
Ejemplo: Sistema de Inventario para Restaurante
JSON de Configuración Completo
{
"version": "1.0.0",
"generatedAt": "2025-01-15T10:30:00Z",
"scenario": {
"id": "restaurant-inventory-v1",
"name": "Sistema de Inventario para Restaurante",
"description": "Gestión de inventario y alertas de stock",
"industry": "restaurant",
"tags": ["inventory", "food-service"]
},
"generatedFrom": {
"source": "ai",
"originalPrompt": "Sistema de inventario para restaurante con alertas de stock bajo",
"generatedAt": "2025-01-15T10:30:00Z"
},
"modules": [
{ "moduleId": "inventory", "enabled": true, "config": { "lowStockThreshold": 10 } },
{ "moduleId": "reports", "enabled": true },
{ "moduleId": "user-management", "enabled": false }
],
"navigation": {
"position": "left",
"collapsible": true,
"showBreadcrumbs": true,
"groups": [
{
"id": "main",
"label": "Principal",
"order": 1,
"visible": true,
"items": [
{
"id": "dashboard",
"label": "Dashboard",
"icon": "dashboard",
"route": "/",
"order": 1,
"moduleId": "core",
"visible": true,
"enabled": true
},
{
"id": "inventory",
"label": "Inventario",
"icon": "inventory",
"route": "/inventory",
"order": 2,
"moduleId": "inventory",
"visible": true,
"enabled": true
}
]
}
]
},
"pages": [
{
"id": "dashboard",
"route": "/",
"title": "Dashboard de Inventario",
"icon": "dashboard",
"layout": { "type": "grid", "columns": 12, "gap": "1rem" },
"widgets": [
{
"id": "low-stock-alerts",
"widgetTypeId": "alert-list-widget",
"displayName": "Alertas de Stock Bajo",
"position": { "x": 0, "y": 0, "width": 6, "height": 2 },
"enabled": true,
"bindings": [
{
"name": "items",
"type": "array",
"required": true,
"source": "repository",
"sourceConfig": {
"type": "repository",
"entityName": "InventoryItem",
"method": "findLowStock"
},
"reactive": true
}
]
},
{
"id": "stock-value-kpi",
"widgetTypeId": "kpi-card-widget",
"displayName": "Valor Total",
"position": { "x": 6, "y": 0, "width": 3, "height": 1 },
"enabled": true,
"bindings": [
{
"name": "value",
"type": "number",
"source": "repository",
"sourceConfig": {
"type": "repository",
"entityName": "InventoryItem",
"method": "getTotalValue",
"valuePath": "total"
}
},
{
"name": "label",
"type": "string",
"source": "manual",
"sourceConfig": { "type": "manual", "value": "Valor Inventario" }
}
]
}
]
}
],
"dashboard": {
"columns": 12,
"widgets": [],
"availableWidgets": []
},
"topNav": {
"showTenantSelector": true,
"showUserMenu": true,
"showNotifications": true
},
"branding": {
"companyName": "Mi Restaurante",
"primaryColor": "#2E7D32"
},
"theme": {
"baseTheme": "light",
"colors": { "primary": "#2E7D32", "secondary": "#FF8F00" }
}
}
Plan de Implementación
Fase 1: Modelos y Signals
Ubicación: libs/core/module-config/src/lib/
| # | Tarea | Archivo | Prioridad |
|---|---|---|---|
| 1.1 | Crear interfaces AISystemConfiguration, ScenarioConfig, etc. |
models/ai-system-config.models.ts |
🔴 Alta |
| 1.2 | Agregar signals de módulos en AppConfigService |
services/app-config.service.ts |
🔴 Alta |
| 1.3 | Agregar signals de páginas y tema | services/app-config.service.ts |
🔴 Alta |
| 1.4 | Actualizar barrel exports | index.ts |
🔴 Alta |
| 1.5 | Eliminar AppUiConfiguration |
models/app-config.models.ts |
🟡 Media |
Fase 2: Modificar Bootstrap
Ubicación: libs/core/module-integrator/src/lib/bootstrap/
| # | Tarea | Archivo | Prioridad |
|---|---|---|---|
| 2.1 | Remover dependencia de ModuleConfigurationService |
browser-bootstrapper.service.ts |
🔴 Alta |
| 2.2 | Usar AppConfigService.enabledModules() |
browser-bootstrapper.service.ts |
🔴 Alta |
| 2.3 | Actualizar lógica de filtrado de módulos | browser-bootstrapper.service.ts |
🔴 Alta |
Fase 3: Limpieza de Código Legacy
| # | Tarea | Archivo | Prioridad |
|---|---|---|---|
| 3.1 | Eliminar ModuleConfigurationService |
module-base/services/ |
🔴 Alta |
| 3.2 | Eliminar endpoint legacy del mock | module-config-mock-api.ts |
🔴 Alta |
| 3.3 | Actualizar barrel exports de module-base | module-base/index.ts |
🔴 Alta |
| 3.4 | Buscar y eliminar imports huérfanos | Proyecto completo | 🟡 Media |
Fase 4: Componente de Página Dinámica
Ubicación: libs/core/ui-framework/src/lib/dynamic/
| # | Tarea | Archivo | Prioridad |
|---|---|---|---|
| 4.1 | Crear DynamicPageComponent |
dynamic-page.component.ts |
🔴 Alta |
| 4.2 | Crear DynamicWidgetHostDirective |
dynamic-widget-host.directive.ts |
🔴 Alta |
| 4.3 | Implementar grid layout dinámico | Dentro de DynamicPageComponent |
🔴 Alta |
| 4.4 | Integrar con DataBindingService |
Resolver bindings | 🔴 Alta |
Fase 5: Widgets de Demo
Ubicación: libs/core/ui-framework/src/lib/widgets/
| # | Tarea | Archivo | Prioridad |
|---|---|---|---|
| 5.1 | Crear KpiCardWidgetComponent |
kpi-card-widget.component.ts |
🔴 Alta |
| 5.2 | Crear AlertListWidgetComponent |
alert-list-widget.component.ts |
🔴 Alta |
| 5.3 | Registrar widgets en registry | widget-registry.ts |
🔴 Alta |
Fase 6: Demo End-to-End
Ubicación: apps/gograb-admin/
| # | Tarea | Archivo | Prioridad |
|---|---|---|---|
| 6.1 | Actualizar mock-api con AISystemConfiguration |
mocks/module-config-mock-api.ts |
🔴 Alta |
| 6.2 | Crear ruta /ai-demo |
features/ai-demo/ |
🔴 Alta |
| 6.3 | Integrar DynamicPageComponent |
Demo route | 🔴 Alta |
| 6.4 | Verificar data bindings E2E | Testing | 🔴 Alta |
Diagrama de Flujo Nuevo
┌─────────────────────────────────────────────────────────────────┐
│ FLUJO DE BOOTSTRAP NUEVO │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. APP_INITIALIZER │
│ └─► BootstrapInitializerService.initializeIfContextExists() │
│ │
│ 2. AppConfigService.loadConfiguration() │
│ └─► HTTP GET: /api/config/ui │
│ └─► Retorna: AISystemConfiguration (TODO UNIFICADO) │
│ │
│ 3. BrowserBootstrapperService.initialize() │
│ │ │
│ ├─► Lee módulos: AppConfigService.enabledModules() │
│ │ └─► Signal: ModuleActivationConfig[] │
│ │ │
│ ├─► Filtra: modules.filter(m => m.enabled) │
│ │ │
│ ├─► ModuleDiscoveryService.discoverAndRegister() │
│ │ │
│ ├─► ModuleLifecycleService.executeLifecycle() │
│ │ │
│ └─► RouteRegistrationService.registerModuleRoutes() │
│ │
│ 4. Componentes consumen signals de AppConfigService │
│ ├─► SidebarComponent: navigationConfig() │
│ ├─► DashboardComponent: dashboardConfig() │
│ └─► DynamicPageComponent: pagesConfig() │
│ │
└─────────────────────────────────────────────────────────────────┘
Beneficios del Enfoque Directo
| Aspecto | Antes | Después |
|---|---|---|
| Servicios de config | 2 (ModuleConfigurationService + AppConfigService) | 1 (AppConfigService) |
| Endpoints API | 2 (modules + ui config) | 1 (/api/config/ui) |
| Código adapter | Necesario para conversión | Eliminado |
| Complejidad | Alta (fallbacks, conversiones) | Baja (flujo directo) |
| Mantenimiento | Difícil (sincronizar 2 fuentes) | Simple (una fuente) |
| Tipos | Múltiples interfaces | Una interface unificada |
Dependencias Entre Bibliotecas
┌─────────────────────────────────────────────────────────────────┐
│ GRAFO DE DEPENDENCIAS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ shared-models ◄─────┬─────────────────────────────────────┐ │
│ │ │ │
│ data-binding ◄──────┼─────────────────────────────────┐ │ │
│ │ │ │ │ │
│ ▼ │ │ │ │
│ module-config ◄─────┴── (AISystemConfiguration) │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ module-integrator ◄───── (usa AppConfigService) │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ui-framework ◄──────────────────────────────────────┬─┘ │ │
│ │ (DynamicPageComponent) │ │ │
│ │ (Widgets) │ │ │
│ ▼ │ │ │
│ gograb-admin ◄──────────────────────────────────────┴─────┘ │
│ (Demo route, Mock API) │
│ │
└─────────────────────────────────────────────────────────────────┘
Consideraciones Futuras (Post-PoC)
Reglas Condicionales
- Sistema de reglas para visibilidad/comportamiento dinámico
- Evaluador de expresiones seguro
- Reglas basadas en permisos, roles, datos
Generación IA
- Integración con LLM para generar configuraciones
- Validación de configuraciones generadas
- Templates base para diferentes industrias
Editor Visual
- UI para modificar configuraciones
- Drag & drop de widgets
- Preview en tiempo real
Persistencia
- Guardar configuraciones por tenant
- Versionado de configuraciones
- Rollback a versiones anteriores
Referencias
Fecha de Creación: 2025-01-15 Última Actualización: 2025-01-15 Estado: Diseño Simplificado - Sin Retrocompatibilidad Próximo Paso: Implementación Fase 1 (Modelos y Signals)