ADR 007: Persistencia multi-tenant (PostgreSQL por plugin)¶
Estado¶
Aceptado — 2026-06 · Diseño de ingeniería (sin implementación aún)
Relacionado con ADR 001 (stack HIVE) y ADR 008 (primer plugin con persistencia real).
Contexto¶
HIVE declara PostgreSQL 16 con una base de datos física por tenant (cortex_{tenant}), alineado con aislamiento de datos (Habeas Data / Ley 1581). Hoy:
CORTEX_DATABASE_URL_TEMPLATEytenant_database_url()existen enframework/cortex_framework/tenant/database.py.- Los plugins de dominio usan fixtures en memoria; no hay capa ORM ni migraciones activas.
alembic_stub.pydocumenta la intención futura sin ejecutar migraciones.
Se requiere un modelo claro: cada plugin es dueño de su esquema de datos; el framework solo resuelve la URL por tenant y orquesta el aprovisionamiento.
Decisión¶
Aislamiento por tenant¶
- Una base de datos física por tenant:
cortex_{tenant}(nombre sanitizado desdeX-Tenant-Id). - Sin schema compartido entre tenants en la misma instancia PG (salvo BD de sistema/admin para provisioning).
Responsabilidades¶
| Capa | Responsabilidad |
|---|---|
| framework | tenant_database_url(), factory de sesión async, hook de provisioning (crear BD + invocar migraciones de plugins habilitados) |
| plugin | Modelos SQLAlchemy 2 async, alembic/, seeds de dev opcionales |
| core | Sin dependencia de ORM |
Convención de tablas¶
- Tablas en schema
publiccon prefijo por plugin:booking_resources,booking_slots,booking_bookings. - Cada plugin mantiene su propio
alembic.iniy cadena de revisiones independiente. - Sin ORM compartido en framework; utilidad mínima:
get_async_session(tenant) -> AsyncSession.
Provisioning de tenant¶
- Alta de tenant (API admin o script ops).
CREATE DATABASE cortex_{tenant}.- Para cada plugin en
CORTEX_ENABLED_PLUGINS:alembic upgrade headcontra la URL del tenant. - Registro de versión de migración por plugin (tabla
cortex_migrationsen framework o metadata Alembic por plugin).
Runtime¶
TenantMiddlewarefija el tenant en contexto.- Handler del plugin obtiene sesión vía factory con
tenant_database_url(). - Transacciones por request; sin estado de sesión global.
Leyenda: Provisioning de BD por tenant y sesión en runtime (módulo Dependiente tenant). Estado: Diseño.
flowchart TB
subgraph provision [Provisioning tenant]
Admin[Alta tenant]
CreateDB["CREATE DATABASE cortex_tenant"]
MigPlugin[Alembic por plugin habilitado]
end
subgraph runtime [Request]
MW[TenantMiddleware]
URL[tenant_database_url]
Session[AsyncSession plugin]
end
Admin --> CreateDB --> MigPlugin
MW --> URL --> Session Leyenda: Flujo alta tenant → migraciones por plugin. Estado: Diseño.
flowchart LR
subgraph framework [cortex_framework]
TURL[tenant_database_url]
Factory[get_async_session]
Prov[TenantProvisioner]
end
subgraph plugins [cortex_plugin_*]
Models[models.py]
Alembic[alembic/]
end
subgraph pg [PostgreSQL 16]
DB1[(cortex_tenant_a)]
DB2[(cortex_tenant_b)]
end
Prov --> Alembic
TURL --> Factory
Factory --> Models
Models --> DB1
Models --> DB2 Leyenda: Framework resuelve URL; cada plugin Independiente posee modelos y Alembic. Estado: Diseño.
Migración desde fixtures¶
- Desarrollo: seed opcional desde JSON actual (
fixtures/) tras migraciones. - Producción: sin
_STOREen memoria; datos solo en PG.
Variables de entorno¶
| Variable | Rol |
|---|---|
CORTEX_DATABASE_URL_TEMPLATE | postgresql+asyncpg://user:pass@host:5432/cortex_{tenant} |
CORTEX_DEFAULT_TENANT | Tenant para /ready y dev local |
Consecuencias¶
- Positivas: aislamiento legal por tenant, plugins desacoplados en esquema, Alembic independiente por dominio.
- Negativas: N bases de datos que administrar; provisioning debe ser idempotente y observable.
- El plugin booking será el primero en adoptar este ADR (ver ADR 008).
Referencias¶
framework/cortex_framework/tenant/database.pyframework/cortex_framework/tenant/alembic_stub.py- ADR 009 (
/readycomprueba PG)