Shared Models Plan

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

Librería Shared Models (Core) - Plan de Desarrollo

Propósito: Crear una librería de modelos y tipos compartidos que sirva como fuente única de verdad para todas las interfaces de datos en el workspace GoGrab.

Estado Planificado

Arquitectura Planificada

La librería implementará un sistema de modelos TypeScript puros organizados por dominios funcionales, con exportaciones granulares para optimización de bundle size y máxima reutilización.

Principios de Diseño

Estructura del Paquete Planificada

libs/core/shared-models/
├── project.json                    # Configuración Nx
├── package.json                    # Dependencias
├── tsconfig.json                   # Config TypeScript base
├── tsconfig.lib.json               # Config para build
├── tsconfig.spec.json              # Config para tests
├── README.md                       # Documentación principal
├── src/
│   ├── lib/
│   │   ├── tenant/                 # 📁 Dominio Tenant
│   │   │   ├── context.ts          # Context interface
│   │   │   ├── config.ts           # Config, UiConfig, ResourceLimits
│   │   │   ├── permissions.ts      # Permissions, ModulePermissions
│   │   │   ├── features.ts         # Features interface
│   │   │   ├── context-info.ts     # ContextInfo para login
│   │   │   └── index.ts            # Exportaciones tenant
│   │   ├── auth/                   # 📁 Dominio Authentication
│   │   │   ├── user-profile.ts     # UserProfile interface
│   │   │   ├── auth-state.ts       # AuthState union types
│   │   │   ├── login-credentials.ts # LoginCredentials interface
│   │   │   ├── login-result.ts     # LoginResult interface
│   │   │   ├── selection-result.ts # SelectionResult interface
│   │   │   ├── auth-errors.ts      # AuthError types
│   │   │   └── index.ts            # Exportaciones auth
│   │   ├── rbac/                   # 📁 Dominio RBAC
│   │   │   ├── role.ts             # Role interface
│   │   │   ├── permission.ts       # Permission interface (simplificado)
│   │   │   └── index.ts            # Exportaciones rbac
│   │   ├── ui/                     # 📁 Dominio UI
│   │   │   ├── branding-config.ts  # BrandingConfig interface
│   │   │   ├── module-config.ts    # ModuleConfig interface
│   │   │   ├── user-preferences.ts # UserPreferences interface
│   │   │   ├── theme-config.ts     # ThemeConfig interface
│   │   │   ├── ui-config.ts        # UiConfig interface
│   │   │   └── index.ts            # Exportaciones ui
│   │   ├── api/                    # 📁 Dominio API
│   │   │   ├── api-response.ts     # ApiResponse<T> generic
│   │   │   ├── pagination.ts       # PaginationRequest, PaginationResponse
│   │   │   ├── error-response.ts   # ErrorResponse interface
│   │   │   ├── request-metadata.ts # RequestMetadata interface
│   │   │   ├── filter-criteria.ts  # FilterCriteria interface
│   │   │   └── index.ts            # Exportaciones api
│   │   ├── common/                 # 📁 Dominio Common
│   │   │   ├── base-entity.ts      # BaseEntity interface
│   │   │   ├── audit-fields.ts     # AuditFields interface
│   │   │   ├── date-range.ts       # DateRange interface
│   │   │   ├── key-value.ts        # KeyValue<T> interface
│   │   │   └── index.ts            # Exportaciones common
│   │   └── index.ts                # Exportación principal (barril)
│   └── test-setup.ts               # Setup para tests
└── tools/
    ├── type-check.ts               # Validación de tipos
    └── docs-generator.ts           # Generador de documentación

Plan de Desarrollo por Pasos

Paso 1: Creación de la Estructura Base

# Crear librería TypeScript
nx generate @nx/js:library shared-models \
  --directory=libs/core \
  --importPath=@libs-core/shared-models \
  --strict \
  --bundler=tsc \
  --minimal

Paso 2: Configuración TypeScript Estricta

// tsconfig.lib.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "strict": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["src/**/*.spec.ts"]
}

Paso 3: Implementación del Dominio Tenant

// src/lib/tenant/context.ts
/**
 * Contexto completo del tenant para el cliente Angular.
 * Contiene toda la información necesaria para personalizar la experiencia del usuario.
 */
export interface Context {
  /** Identificador único del tenant */
  tenantId: string;
  /** Nombre descriptivo del tenant para mostrar en UI */
  tenantName: string;
  /** URL base para las APIs específicas del tenant */
  apiBaseUrl: string;
  /** Locale para internacionalización (ej: 'es-ES', 'en-US') */
  locale: string;
  /** Zona horaria del tenant (ej: 'America/Mexico_City') */
  timezone: string;
  /** Permisos y autorizaciones del usuario en este tenant */
  permissions: Permissions;
  /** Configuración específica del tenant */
  configuration: Config;
  /** Características habilitadas para este tenant */
  features: Features;
}

// src/lib/tenant/config.ts
/**
 * Configuración específica del tenant.
 * Define cómo se comporta la aplicación para este tenant.
 */
export interface Config {
  /** Configuración de módulos habilitados */
  modules: Record<string, ModuleConfig>;
  /** Configuración de interfaz de usuario */
  ui: UiConfig;
  /** Límites y restricciones del tenant */
  limits: ResourceLimits;
}

/**
 * Configuración de interfaz de usuario por tenant.
 */
export interface UiConfig {
  /** Tema visual del tenant */
  theme: string;
  /** Configuración de marca y branding */
  branding: BrandingConfig;
  /** Configuración de navegación */
  navigation?: NavigationConfig;
}

/**
 * Límites de recursos por tenant.
 */
export interface ResourceLimits {
  /** Número máximo de usuarios */
  maxUsers: number;
  /** Almacenamiento máximo en MB */
  maxStorage: number;
  /** Límite de requests por minuto */
  rateLimit: number;
  /** Número máximo de roles personalizados */
  maxRoles: number;
}

Paso 4: Implementación del Dominio Authentication

// src/lib/auth/user-profile.ts
/**
 * Perfil completo del usuario en el contexto de un tenant específico.
 */
export interface UserProfile {
  /** Identificador único del usuario */
  id: string;
  /** ID del tenant al que pertenece este usuario */
  tenantId: string;
  /** Email del usuario (único por tenant) */
  email: string;
  /** Nombre de usuario (único por tenant) */
  username: string;
  /** Nombre real del usuario */
  firstName: string;
  /** Apellido del usuario */
  lastName: string;
  /** URL del avatar del usuario */
  avatar?: string;
  /** Roles asignados al usuario en este tenant */
  roles: Role[];
  /** Permisos efectivos del usuario en este tenant */
  permissions: Permission[];
  /** Preferencias personales del usuario */
  preferences: UserPreferences;
  /** Si el usuario está activo */
  isActive: boolean;
  /** Fecha del último login */
  lastLogin?: Date;
  /** Si el email está verificado */
  emailVerified: boolean;
  /** Si tiene 2FA habilitado */
  twoFactorEnabled: boolean;
  /** Fecha de creación */
  createdAt: Date;
  /** Fecha de última actualización */
  updatedAt: Date;
}

// src/lib/auth/auth-state.ts
/**
 * Estados posibles del proceso de autenticación.
 */
export type AuthState =
  | 'idle' // Estado inicial
  | 'authenticating' // Autenticando credenciales
  | 'context_selection_required' // Necesita seleccionar tenant
  | 'loading_context' // Cargando contexto del tenant
  | 'authenticated' // Completamente autenticado
  | 'error' // Error en el proceso
  | 'expired'; // Sesión expirada

// src/lib/auth/login-result.ts
/**
 * Resultado del login inicial.
 * Incluye tokens básicos y lista de tenants disponibles.
 */
export interface LoginResult {
  /** Token de acceso inicial */
  accessToken: string;
  /** Token de refresh */
  refreshToken: string;
  /** Información básica del usuario */
  user: UserProfile;
  /** Lista de tenants a los que el usuario tiene acceso */
  availableTenants: ContextInfo[];
}

/**
 * Información básica de un tenant para selección.
 */
export interface ContextInfo {
  /** ID del tenant */
  tenantId: string;
  /** Nombre del tenant */
  tenantName: string;
  /** Rol principal del usuario en este tenant */
  role: string;
  /** Permisos base en este tenant */
  permissions: string[];
}

Paso 5: Implementación del Dominio RBAC

// src/lib/rbac/role.ts
/**
 * Rol dentro de un tenant específico.
 */
export interface Role {
  /** Identificador único del rol */
  id: string;
  /** ID del tenant al que pertenece este rol */
  tenantId: string;
  /** Nombre del rol (ej: 'admin', 'manager', 'user') */
  name: string;
  /** Descripción del rol */
  description: string;
  /** Permisos incluidos en este rol */
  permissions: Permission[];
  /** Si es un rol del sistema o personalizado */
  isSystemRole: boolean;
  /** Si el rol está activo */
  isActive: boolean;
  /** Fecha de creación */
  createdAt: Date;
  /** Fecha de última actualización */
  updatedAt: Date;
}

// src/lib/rbac/permission.ts
/**
 * Permiso simple dentro de un tenant.
 */
export interface Permission {
  /** Identificador único del permiso */
  id: string;
  /** ID del tenant al que pertenece este permiso */
  tenantId: string;
  /** Nombre del permiso (ej: 'users:read', 'orders:write', 'reports:export') */
  name: string;
  /** Recurso sobre el que actúa (ej: 'users', 'orders', 'reports') */
  resource: string;
  /** Acción permitida - puede ser cualquier string (ej: 'read', 'write', 'delete', 'export', 'import', 'approve') */
  action: string;
  /** Descripción del permiso */
  description?: string;
  /** Si es un permiso del sistema o personalizado */
  isSystemPermission?: boolean;
}

Paso 6: Implementación del Dominio API

// src/lib/api/api-response.ts
/**
 * Respuesta estándar de la API.
 */
export interface ApiResponse<T = any> {
  /** Datos de respuesta */
  data: T;
  /** Mensaje informativo */
  message?: string;
  /** Código de estado HTTP */
  statusCode: number;
  /** Metadatos adicionales */
  meta?: ResponseMetadata;
}

/**
 * Metadatos de respuesta.
 */
export interface ResponseMetadata {
  /** Timestamp de la respuesta */
  timestamp: string;
  /** ID de la request para tracking */
  requestId: string;
  /** Versión de la API */
  version: string;
}

// src/lib/api/pagination.ts
/**
 * Request de paginación estándar.
 */
export interface PaginationRequest {
  /** Página actual (base 1) */
  page: number;
  /** Elementos por página */
  limit: number;
  /** Campo para ordenamiento */
  sortBy?: string;
  /** Dirección del ordenamiento */
  sortOrder?: 'asc' | 'desc';
  /** Filtros de búsqueda */
  filters?: Record<string, any>;
}

/**
 * Respuesta paginada estándar.
 */
export interface PaginationResponse<T> {
  /** Elementos de la página actual */
  items: T[];
  /** Información de paginación */
  pagination: PaginationInfo;
}

/**
 * Información de paginación.
 */
export interface PaginationInfo {
  /** Página actual */
  currentPage: number;
  /** Elementos por página */
  itemsPerPage: number;
  /** Total de elementos */
  totalItems: number;
  /** Total de páginas */
  totalPages: number;
  /** Si hay página anterior */
  hasPrevious: boolean;
  /** Si hay página siguiente */
  hasNext: boolean;
}

Paso 7: Configuración de Exportaciones

// src/lib/index.ts - Exportación principal (barril)
// Tenant domain
export * from './tenant';

// Auth domain
export * from './auth';

// RBAC domain
export * from './rbac';

// UI domain
export * from './ui';

// API domain
export * from './api';

// Common utilities
export * from './common';

// src/lib/tenant/index.ts
export * from './context';
export * from './config';
export * from './permissions';
export * from './features';
export * from './context-info';

// src/lib/auth/index.ts
export * from './user-profile';
export * from './auth-state';
export * from './login-credentials';
export * from './login-result';
export * from './selection-result';
export * from './auth-errors';

Paso 8: Testing y Validación

// src/lib/tenant/context.spec.ts
import { Context } from './context';

describe('Context Interface', () => {
  it('should have all required properties', () => {
    const context: Context = {
      tenantId: 'tenant-123',
      tenantName: 'Test Tenant',
      apiBaseUrl: 'https://api.test.com',
      locale: 'es-ES',
      timezone: 'America/Mexico_City',
      permissions: {
        permissions: [],
        modules: {},
        resources: {},
      },
      configuration: {
        modules: {},
        ui: {
          theme: 'default',
          branding: {
            logo: 'logo.png',
            primaryColor: '#000000',
          },
        },
        limits: {
          maxUsers: 100,
          maxStorage: 1000,
          rateLimit: 1000,
          maxRoles: 10,
        },
      },
      features: {
        advancedReporting: true,
        apiAccess: true,
        integrations: [],
        customBranding: true,
        customRoles: true,
      },
    };

    expect(context.tenantId).toBeDefined();
    expect(context.tenantName).toBeDefined();
    expect(context.apiBaseUrl).toBeDefined();
    // ... más validaciones
  });
});

Criterios de Éxito

Funcionalidad Core

Calidad y Testing

Adopción

Comandos de Desarrollo

# Crear la librería
nx generate @nx/js:library shared-models \
  --directory=libs/core \
  --importPath=@libs-core/shared-models \
  --strict

# Desarrollo
nx serve shared-models --watch

# Testing
nx test shared-models --coverage
nx run shared-models:type-check

# Build y validación
nx build shared-models
nx lint shared-models

# Documentación
nx run shared-models:docs

Migración de Librerías Existentes

Paso 1: Instalar shared-models

// libs/core/security/package.json
{
  "dependencies": {
    "@libs-core/shared-models": "*"
  }
}

Paso 2: Actualizar imports

// Antes
interface Context { ... }
interface UserProfile { ... }

// Después
import { Context, UserProfile } from '@libs-core/shared-models';

Paso 3: Eliminar duplicados

# Eliminar interfaces duplicadas
rm libs/core/security/src/lib/models/context.ts
rm libs/core/security/src/lib/models/user-profile.ts

Esta librería se convierte en la piedra angular del sistema GoGrab, garantizando consistencia de tipos en todo el workspace y facilitando la evolución controlada de los modelos de datos.