--- id: "PMC-001-TENANTS" title: "PMC-001: Módulo de Tenants" type: "Module Definition" epic: "PMC-001" status: "Draft" project: "platform_marketing_content" version: "1.0.0" created_date: "2026-01-04" updated_date: "2026-01-04" --- # PMC-001: Módulo de Tenants **Versión:** 1.0.0 **Fecha:** 2025-12-08 **Estado:** Definición **Prioridad:** Alta --- ## Descripción General El módulo de Tenants proporciona la arquitectura multi-tenant que permite aislar datos y configuraciones entre diferentes organizaciones (agencias) que utilicen la plataforma. --- ## Objetivos 1. Aislar completamente los datos entre tenants 2. Permitir configuración personalizada por tenant 3. Soportar branding personalizado (logo, colores) 4. Gestionar límites y cuotas por tenant 5. Preparar arquitectura para comercialización SaaS futura --- ## Entidades del Dominio ### Tenant ```yaml Entidad: Tenant Descripción: Organización/agencia que utiliza la plataforma Atributos: - id: UUID (PK) - name: string (nombre de la organización) - slug: string (identificador URL-friendly, único) - status: enum [active, suspended, trial, cancelled] - plan_id: UUID (FK a Plan) - settings: JSONB (configuración personalizada) - branding: JSONB (logo_url, primary_color, secondary_color) - limits: JSONB (cuotas y límites) - created_at: timestamp - updated_at: timestamp - deleted_at: timestamp (soft delete) Relaciones: - 1:N con User - 1:N con Client (CRM) - 1:N con Project - 1:N con Asset - N:1 con Plan ``` ### Plan ```yaml Entidad: Plan Descripción: Plan de suscripción con límites definidos Atributos: - id: UUID (PK) - name: string (Free, Pro, Enterprise, Internal) - code: string (identificador único) - features: JSONB (funcionalidades habilitadas) - limits: JSONB - generations_per_month: number - trainings_per_month: number - storage_gb: number - users_max: number - projects_max: number - price_monthly: decimal - price_yearly: decimal - is_active: boolean - created_at: timestamp - updated_at: timestamp Relaciones: - 1:N con Tenant ``` ### TenantSettings ```yaml Estructura JSONB: settings Campos: - default_language: string (es, en) - timezone: string - date_format: string - currency: string - notifications: email_enabled: boolean slack_webhook: string - integrations: crm_external_url: string n8n_webhook_base: string - generation: default_model: string default_quality: string watermark_enabled: boolean ``` --- ## Funcionalidades ### F-001.1: Gestión de Tenants | ID | Funcionalidad | Descripción | Prioridad | |----|---------------|-------------|-----------| | F-001.1.1 | Crear tenant | Registro de nueva organización | Alta | | F-001.1.2 | Editar tenant | Modificar datos y configuración | Alta | | F-001.1.3 | Suspender tenant | Desactivar acceso temporalmente | Media | | F-001.1.4 | Eliminar tenant | Soft delete con retención de datos | Baja | ### F-001.2: Configuración por Tenant | ID | Funcionalidad | Descripción | Prioridad | |----|---------------|-------------|-----------| | F-001.2.1 | Branding | Personalizar logo y colores | Media | | F-001.2.2 | Límites | Configurar cuotas de uso | Alta | | F-001.2.3 | Integraciones | URLs de webhooks y APIs externas | Media | | F-001.2.4 | Preferencias | Idioma, zona horaria, formatos | Baja | ### F-001.3: Aislamiento de Datos | ID | Funcionalidad | Descripción | Prioridad | |----|---------------|-------------|-----------| | F-001.3.1 | RLS PostgreSQL | Row-Level Security por tenant_id | Alta | | F-001.3.2 | Middleware | Inyección automática de tenant_id | Alta | | F-001.3.3 | Storage isolation | Prefijos S3 por tenant | Alta | --- ## Reglas de Negocio ```yaml RN-001.1: Descripción: Todo tenant debe tener un plan asignado Validación: plan_id NOT NULL Default: Plan "Free" o "Internal" RN-001.2: Descripción: El slug del tenant debe ser único y URL-safe Validación: Regex ^[a-z0-9-]+$, único en BD RN-001.3: Descripción: Tenant suspendido no permite login de usuarios Acción: Validar status en middleware de autenticación RN-001.4: Descripción: Límites del plan se heredan al tenant Override: Tenant puede tener límites personalizados que sobreescriban el plan RN-001.5: Descripción: Soft delete conserva datos 90 días Acción: deleted_at marca fecha, cron job limpia después ``` --- ## API Endpoints ```yaml Base: /api/v1/tenants Endpoints: POST /tenants: Descripción: Crear nuevo tenant (Super Admin) Body: { name, slug, plan_id, settings? } Response: 201 Created GET /tenants: Descripción: Listar tenants (Super Admin) Query: ?status=active&page=1&limit=20 Response: 200 OK (paginado) GET /tenants/:id: Descripción: Obtener tenant por ID Response: 200 OK PUT /tenants/:id: Descripción: Actualizar tenant Body: { name?, settings?, branding?, limits? } Response: 200 OK PATCH /tenants/:id/status: Descripción: Cambiar estado del tenant Body: { status: "suspended" | "active" } Response: 200 OK DELETE /tenants/:id: Descripción: Soft delete tenant Response: 204 No Content GET /tenants/current: Descripción: Obtener tenant del usuario actual Response: 200 OK ``` --- ## Dependencias ```yaml Dependencias del Catálogo: - @CATALOG_TENANT: path: shared/catalog/multi-tenancy/ uso: Patron base multi-tenancy con RLS adaptar: - Extender modelo Tenant con campos branding - Agregar sistema de quotas por plan - Integrar con limites de generacion docs: shared/catalog/multi-tenancy/README.md - @CATALOG_PAYMENTS: path: shared/catalog/payments/ uso: Suscripciones y billing (Fase 4) adaptar: - Crear productos/precios en Stripe - Definir planes PMC (Starter, Pro, Business, Enterprise) - Integrar creditos de generacion docs: shared/catalog/payments/README.md Dependencias de Módulos: - PMC-007 Admin: Gestión de planes y configuración global Servicios Externos: - Stripe (Fase 4 - SaaS comercial) Referencia de Implementacion: - Proyecto origen: projects/gamilit/apps/backend/src/modules/auth/entities/tenant.entity.ts - RLS policies: projects/gamilit/apps/database/ddl/policies/ ``` --- ## Consideraciones Técnicas ### Estrategia Multi-Tenant ```yaml Tipo: Single Database, Shared Schema Aislamiento: Row-Level Security (RLS) en PostgreSQL Implementación: 1. Columna tenant_id en todas las tablas principales 2. Políticas RLS que filtran por tenant_id 3. Middleware que extrae tenant del JWT/sesión 4. SET app.current_tenant antes de cada query ``` ### Ejemplo RLS PostgreSQL ```sql -- Política para tabla clients CREATE POLICY tenant_isolation_policy ON clients USING (tenant_id = current_setting('app.current_tenant')::uuid); -- Habilitar RLS ALTER TABLE clients ENABLE ROW LEVEL SECURITY; ``` ### Storage Isolation ```yaml Estructura S3: bucket/ ├── {tenant_slug}/ │ ├── assets/ │ ├── generated/ │ ├── models/ │ └── temp/ ``` --- ## Criterios de Aceptación - [ ] Tenant puede ser creado con datos mínimos (name, slug, plan) - [ ] RLS impide acceso a datos de otros tenants - [ ] Branding se refleja en UI del tenant - [ ] Límites del plan se validan antes de operaciones - [ ] Soft delete funciona correctamente - [ ] API responde correctamente a todos los endpoints --- ## Referencias - [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md) - [@CATALOG_TENANT](../../../shared/catalog/modules/multi-tenancy/) --- **Documento generado por:** Requirements-Analyst **Fecha:** 2025-12-08