Hoja de ruta
Estado de cada fase del plan (v3). [x] = hecho y verificado con tests; [ ] = pendiente.
Fase 0 — Base del proyecto ✅
- [x] Monorepo con pnpm, Turborepo, tsup, Vitest, ESLint, Changesets
- [x] dependency-cruiser con las reglas de capas
- [x] CI en GitHub Actions (Node 20 / 22)
- [x] Docker Compose con MySQL, Postgres, Valkey y MinIO
- [x] Primeros ADRs (arquitectura hexagonal, Kysely, AsyncLocalStorage, DI manual, observabilidad)
Fase 1 — Núcleo ✅
- [x] Dominio:
Tenant,Domain,Theme, value objects y errores - [x] Puertos (interfaces)
- [x] Contexto con
AsyncLocalStorage(run,current,central,runForEach) - [x] Resolvers y cadena
- [x]
CachedTenantRepository(decorator con LRU) +CachedDomainRepository - [x]
EventBusen proceso con modossyncyasync - [x] Facade
tenancyy composition root - [x] Adaptadores Fastify, Express y
node:http - [x] Paquete
testingcon tests de contrato - [x] Tests de aislamiento concurrente
- [x] Observabilidad: logs estructurados con
tenantId, registro de errores por tenant (ADR 0005)
Fase 2 — Base de datos ✅
Verificado contra motores reales con Testcontainers: MySQL 8, MariaDB 11 y PostgreSQL 16 (24 tests de integración cada uno).
- [x] Migraciones del paquete: todas las tablas
tenancy_*para MySQL y PostgreSQL - [x] Repositorios de tenants, dominios y servidores (pasan las mismas suites de contrato que los de memoria)
- [x] Cifrado AES-256-GCM de credenciales con rotación de llaves (
database.rotateKey()) - [x] Integración Kysely y
ConnectionPoolRegistrycon LRU por servidor - [x] Driver MySQL/MariaDB (base por tenant, credenciales compartidas o por tenant)
- [x] Driver PostgreSQL (base por tenant, credenciales compartidas o por tenant)
- [x] Pipeline de aprovisionamiento idempotente, con candados, estados, reintentos y limpieza
- [x] Estrategias de ubicación en varios servidores (
least-tenants,weighted,fixed, función propia) - [x]
centralDb(), acceso entre tenants ytenancy.sql(SQL puro parametrizado) - [x] Puerto
Migratorcon Kysely y archivos.sql - [x] Ejemplo
examples/fastify-mysqlfuncionando
Fase 3 — CLI ✅
Probado de punta a punta contra PostgreSQL real, también como binario (node dist/bin.js). Detalles en docs/cli.md y ADR 0007.
- [x]
init(con detección de framework, ORM, lenguaje, tipo de módulos, motor y gestor de paquetes),install,create,list,migrate,rollback,seed,run,delete - [x]
servers:add,servers:list, creación masiva desde CSV (validación previa, concurrencia,--dry-run),key:rotate - [x] Extras:
migrate:status,key:generate,--json,--log-file,--verbose, códigos de salida, confirmación endelete
Hito v0.1 ✅: usable en un proyecto real con MySQL o Postgres.
Fase 4 — Caché, archivos, colas y tema ✅
Verificado contra servicios reales con Testcontainers: Valkey 8 (caché y BullMQ), SeaweedFS (S3) y PostgreSQL 16 (Knex y Drizzle). Detalles en ADR 0008.
- [x] Caché: Redis/Valkey (
SCAN+UNLINKpara vaciar un tenant), memoria yremember() - [x] Archivos: local, memoria y S3 (URLs públicas o firmadas), con rutas protegidas contra
../ - [x] Colas: BullMQ y memoria,
tenancy.jobs,tenancy.worker(), modoqueuede eventos y comandotenancy worker - [x] Tema por tenant: helpers (
toCss,toJson, ETag) y caché - [x] Ruta
/tenancy/theme.css(ETag y 304) - [x]
/tenancy/me(solopublicFields) - [x] Rutas HTTP opcionales (
http.me,http.theme,http.assets,http.health), apagadas por defecto, en Fastify, Express y node:http - [x] Integraciones ORM: Prisma, Drizzle y Knex (instancia por tenant con LRU, y sus migradores)
- [x]
tenancy schemapara generar modelos de las tablas centrales (Prisma, Drizzle y TypeORM) - [ ] Colas separadas por tenant (hoy: cola compartida con
tenantIden cada trabajo) - [ ] Prueba de
orm-prismacontra un cliente Prisma generado real (hoy: cliente de reemplazo)
Fase 5 — Eventos avanzados ✅
Verificado contra servicios reales: PostgreSQL 16 y MySQL 8 (outbox), RabbitMQ 4 y Valkey 8 (Redis Streams), y un servidor HTTP que verifica las firmas. Detalles en ADR 0009.
- [x] Sobre CloudEvents y catálogo tipado (
TenancyEventMap+ esquemas Valibot para eventos propios) - [x] Outbox + relay con
SKIP LOCKED(varios relays en paralelo, lease, dead-letter, reenvío, limpieza) - [x] Transporte webhook con HMAC, reintentos, circuit breaker y dead-letter (más SSRF y timeouts)
- [x] Transportes Redis Streams y RabbitMQ (con consumidores)
- [x] Ejemplo
microservicio-emails(probado de punta a punta, idempotente) - [x] CLI:
outbox:relay,outbox:status,outbox:retry
Hito v0.3 ✅: eventos listos para producción.
Fase 6 — Admin API ✅
Probada por HTTP real contra PostgreSQL 16 (sin sesión, sin CSRF, con el rol equivocado, desde el host de un tenant, con bodies inválidos). Detalles en ADR 0010.
- [x] Autenticación (argon2id, sesiones con cookie o Bearer, CSRF, límite de intentos, 2FA TOTP), roles y auditoría
- [x] Endpoints de tenants, dominios, tema, migraciones y caché
- [x] Progreso en vivo (SSE)
- [x] Explorador de datos (solo lectura, columnas sensibles ocultas)
- [x] Webhooks, outbox y dead-letter
- [x] Impersonación (tokens de un solo uso)
- [x] Documentación OpenAPI 3.1
- [x]
admin:serveen proceso y puerto separados (yadmin:user); montaje en Express y Fastify - [ ] Edición de datos desde el explorador (por rol y con auditoría)
Fase 7 — Admin UI ✅
Probada en Chromium real contra PostgreSQL (Playwright), con CSP estricta y sin errores en la consola. Detalles en ADR 0011.
- [x] Login (con 2FA y aviso de bloqueo) y layout
- [x] Dashboard con métricas (tenants por estado, salud, outbox, pools y errores por tenant)
- [x] Tenants: lista con búsqueda y filtros, creación con progreso en vivo y detalle
- [x] Editor de tema con vista previa
- [x] Dominios, migraciones y explorador de datos
- [x] Webhooks e historial de entregas (y dead-letter de la outbox)
- [x] Usuarios y auditoría (y "Mi cuenta": contraseña y 2FA)
- [ ] Traducción a otros idiomas (hoy solo español)
Hito v0.5 ✅: paquete con panel de administración completo.
Fase 8 — Más drivers y transportes ✅
Verificado contra servicios reales con Testcontainers: PostgreSQL 16 (base y schema), SQLite, SQL Server 2022, MySQL 8, Memcached 1.6, Kafka 3.9, NATS 2 (JetStream) y Valkey 8. Detalles en los ADR 0012 a 0015.
- [x] PostgreSQL modo schema (con rol por tenant que no puede leer otros schemas)
- [x] SQLite
- [x] SQL Server
- [x] Memcached
- [x] Transportes Kafka y NATS (con consumidores)
- [x] Invalidación entre instancias con Redis pub/sub
- [x] OpenTelemetry y métricas Prometheus
- [x] Integraciones ORM: TypeORM, Sequelize y MikroORM
- [x]
tenancy movepara mover un tenant entre servidores - [ ]
tenancy movepara SQL Server y SQLite, y test propio en modo schema - [ ]
tenancy schemapara SQLite y SQL Server; Drizzle en modo schema
Hito v0.8 ✅: cuatro motores, cinco transportes, seis ORMs y telemetría.
Fase 9 — Publicación
Paquetes verificados como se publican: pnpm pack, publint, arethetypeswrong y una app de humo instalada con npm fuera del monorepo (ESM, CJS y el binario tenancy). Detalles en scripts/check-packages.mjs.
- [x] Sitio de documentación con guías y referencia (VitePress: 15 guías con ejemplos que compilan, referencia de la API con TypeDoc y los ADR; se publica en GitHub Pages)
- [x] Ejemplos funcionando en
examples/(4, cada uno con su test contra el motor real) - [x] Benchmarks publicados (núcleo, costo por petición en HTTP y carga con base de datos)
- [x] Prueba de carga con 10 000 tenants (PostgreSQL por schema: 10 000 creados en 55 s; 4 551 req/s con una consulta por petición y 0 respuestas con el tenant equivocado en 197 337)
- [x] Metadatos de publicación, CHANGELOG y workflows de CI, documentación y release (changesets con provenance)
- [ ] Publicar en npm (0.8.0)
Hito v1.0: versión estable.
Fase 10 — Planes, módulos, avisos y cobros (v0.11) ✅
Objetivo: que cualquier proyecto declare sus módulos y planes en la configuración y la librería haga cumplir todo lo demás (API, menú, límites, panel y precios públicos). La librería no conoce módulos concretos ("reportes", "caja"): los nombres los pone cada proyecto.
Lo que ya existía (0.10)
- [x] Campo
planen cada tenant (texto libre), editable desde el panel y la CLI - [x]
/tenancy/mepuede mostrarplanconpublicFields - [x] Tema por tenant y
/tenancy/theme.css(la fuente era un nombre libre, sin carga) - [x] Invalidación de la caché de búsqueda entre instancias al guardar un tenant
- [x] Panel con tenants, editor de tema, usuarios de cada tenant, auditoría y límite de intentos
Lo que se agregó
Núcleo (@tenancy-node/core)
- [x] Configuración
features(catálogo de módulos) yplans(módulos, límites, precio,public), condefaultPlan - [x] El plan de un tenant se valida contra el catálogo; uno desconocido no rompe nada (sin módulos y aviso en el log)
- [x] Módulos sueltos por tenant (
features: { add, remove, limits }), guardados en la base central - [x]
tenancy.features:has,list,limit,assertLimit,require,of - [x]
tenancy.plans:list,get - [x] Errores
TENANCY_FEATURE_NOT_IN_PLAN(403) yTENANCY_PLAN_LIMIT_REACHED(403), con detalles en la respuesta - [x] Evento
tenant.plan_changed - [x]
/tenancy/meconfeaturesylimits; ruta públicaGET /tenancy/plans - [x] Tipografías:
theme.fonts(Google, sistema o URL propia), validación y carga dentro detheme.css - [x] Nombres de módulo con autocompletado en TypeScript (declaration merging, opcional)
Base de datos (@tenancy-node/db)
- [x] Migración: columna
featuresentenancy_tenantsy tablatenancy_leads - [x] Repositorio de solicitudes (leads) y conteo de tenants por plan
Frameworks
- [x]
requireFeature()en Express y Fastify;tenancy.features.require()paranode:http
Panel (admin-api + admin-ui)
- [x] Plan del tenant: selector de plan, casillas de módulos sueltos y límites propios
- [x] Página "Planes": módulos, precios y cuántos tenants tiene cada plan
- [x] Página "Solicitudes": formulario público
POST /admin/api/leads(campo trampa, límite por IP, consentimiento), estados y botón "Crear tenant" con el plan elegido; eventolead.createdpara avisar por correo - [x] Tipografía: lista con vista previa de cada fuente; vista previa del tema
- [x] Dashboard: tenants por plan y solicitudes nuevas
CLI
- [x]
tenancy plansytenancy tenant:plan <id> <plan> [--add] [--remove]
Avisos y push
- [x]
tenancy.notifications: a todos, a algunos tenants o por plan; roles, vigencia, lecturas por usuario - [x] Dispositivos y canales de entrega;
@tenancy-node/notify-firebase(FCM HTTP v1) - [x] Panel "Avisos" y
tenancy notify
Cobros
- [x]
tenancy.billing: suscripción, facturas, pagos manuales y parciales, ciclo de cobro con suspensión opcional - [x] Interfaz
PaymentProviderpara pasarelas (checkout + webhook idempotente) - [x] Panel "Cobros" y cobros en el detalle del tenant;
tenancy billing:run
Calidad y publicación
Verificado con 556 pruebas del backend contra PostgreSQL 16, MySQL 8, MariaDB 11 y SQLite (contratos de los repositorios nuevos en los cuatro motores), 25 de la interfaz (dos en Chromium real) y Firebase contra un servidor que verifica la firma del JWT.
- [x] Pruebas unitarias y de integración en MySQL, MariaDB y PostgreSQL
- [x] Guía "Planes y módulos" con la conexión paso a paso (backend y frontend), ADR, CHANGELOG
- [x] Publicar v0.11.0 (GitHub Release con los .tgz)
Fase 11 — Planes y marca editables desde el panel (v0.12) ✅
- [x] Cambios de planes guardados sobre la configuración (
tenancy_plan_settings), crear, restablecer y archivar - [x] Permisos síncronos con recarga por el bus de invalidación y cada 60 s
- [x] Marca de la plataforma (
tenancy_platform_settings): nombre, logo, favicon, color, contacto y correos de ventas - [x]
GET /tenancy/platformy/tenancy/platform/assets/*públicos;notifyToenlead.created - [x] Panel: edición de planes y página "Plataforma"; CLI con los planes del panel
- [x] Pruebas en memoria y en PostgreSQL, MySQL, MariaDB y SQLite
Pendiente para la app (fuera de la librería): conectar una pasarela de pago real y un proyecto de Firebase real, y la web comercial (Fase 4 del plan de IONIX).