Version: 1.0
Fecha: 25 de Marzo, 2026
Contexto: Analisis tecnico profundo para la integracion del backend FastAPI de DataVault con el ecosistema Simappe (OAuth2, Gateway, multi-tenancy database-per-tenant)
Arquitecto: Carlos Alberto Torres Camargo
Clasificacion: Interno — Arquitectura
Este documento es el orquestador central de la migracion del backend DataVault (FastAPI/Python) hacia el ecosistema Simappe. Define el orden de ejecucion, las dependencias entre dominios, los controles de calidad y los criterios de aceptacion para cada fase.
La migracion se descompone en 5 dominios tecnicos, cada uno documentado en un archivo independiente. Este documento establece la secuencia correcta de implementacion y las validaciones cruzadas entre dominios.
| Aspecto |
DataVault Actual |
Objetivo Simappe |
Impacto |
| Autenticacion |
JWT propio (HS256, SECRET_KEY local, python-jose) |
JWT de SimappeOAuth2 (claims: customerId, companyId, subsidiaryId, environment, roles) |
Alto — Reescritura de utils/auth.py y utils/security.py |
| Multi-tenancy |
Header X-Tenant-ID (UUID) → filtro por tenant_id en BD unica |
Claims JWT → resolucion jerarquica via SimappeAdmin → BD-por-tenant |
Critico — Reescritura de middleware/multitenancy.py y database.py |
| Conexion BD |
DATABASE_URL fija en .env → engine unico con pool de 20-60 conexiones |
Dinamica: credenciales resueltas desde SimappeAdmin /api/v2/database-config/connection → pool por tenant |
Critico — Rediseno completo de database.py |
| Roles |
superadmin, admin_empresa, rh, archivista, auditor, consulta (tabla user_tenants.role) |
Roles extraidos del JWT Simappe (SUPER_ADMIN, ADMIN, USER, AUDITOR) + permisos granulares internos |
Medio — Capa de mapeo en utils/security.py |
| Registro servicio |
No registrado (standalone) |
Eureka client + health endpoint compatible |
Bajo — Nuevo modulo eureka_client.py |
| Gateway |
NGINX directo |
SimappeGateway (/api/vault/**) |
Bajo — Configuracion en Gateway |
| Configuracion |
Variables en .env |
SimappeConfigServer (externalizado) |
Medio — Fase posterior |
Los documentos deben leerse y ejecutarse en este orden porque cada dominio depende de los anteriores:
┌─────────────────────────────────────────────────────────────────────┐
│ FASE 1: AUTENTICACION (Semana 2) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 01 — Autenticacion JWT Simappe │ │
│ │ Validar JWT externo, extraer claims, eliminar auth local │ │
│ └─────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ FASE 2: CONEXION (Semana 2-3) │
│ ┌─────────────────────────▼───────────────────────────────────┐ │
│ │ 02 — Cliente SimappeAdmin + Conexion BD Dinamica │ │
│ │ Consumir /api/v2/database-config/connection │ │
│ │ Pool de conexiones por tenant, cache de 30 minutos │ │
│ └─────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ FASE 3: MULTITENANCY (Semana 3-4) │
│ ┌─────────────────────────▼───────────────────────────────────┐ │
│ │ 03 — Multi-tenancy Database-per-Tenant │ │
│ │ Eliminar X-Tenant-ID, resolver tenant desde JWT, │ │
│ │ inyectar sesion de BD correcta por request │ │
│ └─────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ FASE 4: ROLES (Semana 2-3) │
│ ┌─────────────────────────▼───────────────────────────────────┐ │
│ │ 04 — Mapeo de Roles y Permisos │ │
│ │ Traducir roles JWT Simappe a roles DataVault │ │
│ │ Mantener permisos granulares internos │ │
│ └─────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ FASE 5: VALIDACION (Semana 4+) │
│ ┌─────────────────────────▼───────────────────────────────────┐ │
│ │ 05 — Plan de Pruebas y Validacion │ │
│ │ Tests por capa, integracion E2E, criterios de aceptacion │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
| # |
Documento |
Dominio |
Archivos Afectados |
Esfuerzo |
Semana |
| 1 |
Autenticacion JWT Simappe |
Auth |
utils/auth.py, utils/security.py, api/auth.py, config.py |
Alto |
S2 |
| 2 |
Conexion BD Dinamica |
Datos |
database.py, config.py, nuevo services/simappe_client.py |
Critico |
S2-S3 |
| 3 |
Multi-tenancy Database-per-Tenant |
Tenancy |
middleware/multitenancy.py, models/tenant.py, todos los routers |
Critico |
S3-S4 |
| 4 |
Mapeo de Roles y Permisos |
Seguridad |
utils/security.py, models/user.py, decoradores de autorización |
Medio |
S2-S3 |
| 5 |
Plan de Pruebas y Validacion |
QA |
Tests nuevos, Postman collections, Docker Compose integrado |
Medio |
S4+ |
backend/app/
├── config.py ← FASE 1: Agregar configuracion Simappe
├── main.py ← FASE 1: Actualizar CORS, health endpoint
├── database.py ← FASE 2: Reescribir conexion dinamica
├── utils/
│ ├── auth.py ← FASE 1: Reemplazar JWT propio por validacion Simappe
│ └── security.py ← FASE 1+4: Extraer claims Simappe, mapear roles
├── middleware/
│ └── multitenancy.py ← FASE 3: Eliminar X-Tenant-ID, resolver desde JWT
├── services/
│ └── simappe_client.py ← FASE 2: NUEVO — Cliente HTTP para SimappeAdmin
├── models/
│ ├── user.py ← FASE 3: Eliminar UserTenant si aplica
│ └── tenant.py ← FASE 3: Evaluar eliminacion
└── api/
├── auth.py ← FASE 1: Reducir endpoints (login ya no es local)
└── [todos los routers] ← FASE 3: Cambiar get_db() por get_tenant_db()
graph TD
A[config.py<br>Agregar SIMAPPE_*] --> B[utils/auth.py<br>Validar JWT Simappe]
B --> C[utils/security.py<br>Extraer claims + mapear roles]
A --> D[services/simappe_client.py<br>NUEVO: Cliente SimappeAdmin]
D --> E[database.py<br>Conexion dinamica por tenant]
C --> F[middleware/multitenancy.py<br>Resolver tenant desde JWT]
E --> F
F --> G[api/*.py<br>Inyectar sesion tenant]
C --> H[api/auth.py<br>Reducir endpoints]
F --> I[models/tenant.py<br>Evaluar simplificacion]
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Angular App │───▶│ Simappe │ │ Simappe │ │ PostgreSQL │
│ (Nebula) │ │ Gateway │ │ Admin │ │ (tenant DB) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────────┘ └──────┬───────────┘
│ │ │ │
1. GET /api/vault/files │ │ │
Authorization: Bearer │ │ │
<jwt_simappe> │ │ │
│──────────────────▶│ │ │
│ 2. Route match │ │
│ /api/vault/** ──────▶ │ │
│ │ FastAPI│ │
│ │ │ │
│ 3. Middleware: Validar JWT│ │
│ │ Extraer claims: │ │
│ │ - customerId │ │
│ │ - companyId │ │
│ │ - subsidiaryId │ │
│ │ - environment │ │
│ │ - roles │ │
│ │ │ │
│ 4. GET /api/v2/database- │ │
│ config/connection ───▶│ │
│ │ │ Resolucion │
│ │ │ jerarquica: │
│ │ │ Subsidiary > │
│ │ │ Company > │
│ │ │ Global │
│ │ ◀── {host, port, │ │
│ │ db, schema, │ │
│ │ user, pass} │ │
│ │ │ │
│ 5. Obtener/crear pool │ │
│ para este tenant ─────┼──────────────────────▶│
│ │ │ │
│ 6. Ejecutar query ───────┼──────────────────────▶│
│ │ │ │
│ ◀── 7. Response │ │ │
│ con datos │ │ │
| # |
Riesgo |
Probabilidad |
Impacto |
Mitigacion |
Dominio |
| R1 |
JWT Simappe usa algoritmo/clave diferente al esperado |
Media |
Bloqueante |
Obtener public_key o secreto compartido del SimappeOAuth2Server antes de iniciar. Verificar con JwtHelper.java |
Auth |
| R2 |
Latencia de resolucion de BD via SimappeAdmin |
Baja |
Alto |
Cache local Caffeine/TTL 30 min. SimappeAdmin ya tiene cache interno |
Conexion |
| R3 |
Pool explosion: demasiados tenants = demasiados pools |
Media |
Alto |
Limite de 10 pools concurrentes + LRU eviction. Monitorear con metricas |
Conexion |
| R4 |
Permisos granulares DataVault no mapeables a roles JWT |
Media |
Medio |
Mantener capa de permisos propia (module_permissions table), usar JWT solo para auth base |
Roles |
| R5 |
Migraciones Alembic incompatibles entre tenants |
Alta |
Alto |
Ejecutar migraciones en todas las BDs de tenant en paralelo. Script de despliegue dedicado |
Tenancy |
| R6 |
Modelos SQLAlchemy con tenant_id FK que ya no existe |
Alta |
Medio |
Migración SQL para eliminar columna + actualizar modelos Python. Hacerlo en fase de testing, no antes |
Tenancy |
| R7 |
Downtime durante la transicion auth |
Media |
Alto |
Periodo de gracia: aceptar AMBOS JWTs (propio y Simappe) durante 2 semanas |
Auth |
| Archivo |
Lineas actuales |
Tipo cambio |
Complejidad |
Fase |
config.py |
49 |
Editar |
Baja |
1 |
utils/auth.py |
53 |
Reescribir |
Alta |
1 |
utils/security.py |
87 |
Reescribir |
Alta |
1, 4 |
api/auth.py |
447 |
Reducir (~70%) |
Media |
1 |
services/simappe_client.py |
0 (nuevo) |
Crear |
Alta |
2 |
database.py |
35 |
Reescribir |
Critica |
2 |
middleware/multitenancy.py |
44 |
Reescribir |
Alta |
3 |
models/user.py |
40 |
Editar |
Media |
3 |
models/tenant.py |
~60 |
Evaluar eliminacion |
Media |
3 |
main.py |
85 |
Editar (CORS, health) |
Baja |
1 |
api/*.py (24 routers) |
~600K total |
Editar (get_db→get_tenant_db) |
Media |
3 |
gantt
title Migracion Backend DataVault → Simappe
dateFormat YYYY-MM-DD
section Fase 1: Auth
Configuracion Simappe en config.py :a1, 2026-03-23, 1d
Reescribir utils/auth.py :a2, after a1, 2d
Reescribir utils/security.py :a3, after a2, 2d
Reducir api/auth.py :a4, after a3, 1d
Actualizar main.py (CORS + health) :a5, after a1, 1d
Gate de Aceptacion Fase 1 :milestone, after a4, 0d
section Fase 2: Conexion
Crear services/simappe_client.py :b1, after a2, 3d
Reescribir database.py :b2, after b1, 3d
Gate de Aceptacion Fase 2 :milestone, after b2, 0d
section Fase 3: Tenancy
Reescribir middleware/multitenancy.py :c1, after b2, 2d
Actualizar modelos (tenant_id) :c2, after c1, 2d
Actualizar 24 routers (get_tenant_db) :c3, after c2, 3d
Gate de Aceptacion Fase 3 :milestone, after c3, 0d
section Fase 4: Roles
Implementar mapeo de roles :d1, after a3, 2d
Validar permisos granulares :d2, after d1, 1d
Gate de Aceptacion Fase 4 :milestone, after d2, 0d
section Fase 5: Validacion
Tests unitarios :e1, after c3, 2d
Tests de integracion :e2, after e1, 2d
Tests E2E con Gateway :e3, after e2, 2d
Postman collection :e4, after e3, 1d
Gate de Aceptacion Final :milestone, after e4, 0d
| # |
Decision |
Opcion Elegida |
Justificacion |
| DA-1 |
Mantener FastAPI o reescribir en Spring Boot |
Mantener FastAPI |
El plan de migracion (S1) decidio adaptar, no reescribir. La logica de negocio OAIS es compleja y funcional |
| DA-2 |
Validar JWT localmente o contra SimappeOAuth2 |
Localmente con clave publica |
Evita llamada de red por cada request. La clave publica se obtiene al arrancar el servicio |
| DA-3 |
Cache de conexiones BD: in-memory o Redis |
In-memory (dict + TTL) |
DataVault es un solo proceso. Redis agrega complejidad innecesaria |
| DA-4 |
Periodo de gracia dual-JWT |
Si, 2 semanas |
Permite migracion gradual sin downtime |
| DA-5 |
Eliminar tenant_id de modelos o mantener |
Eliminar |
En modelo database-per-tenant, cada BD ES un tenant. La columna es redundante |
| Version |
Fecha |
Autor |
Descripcion |
| 1.0.0 |
2026-03-25 |
Carlos Torres |
Creacion del documento orquestador con analisis completo de 5 dominios |