FactuGES_ERP/docs/plan-docker-y-migraciones-bd.md

218 lines
12 KiB
Markdown

# Plan de Docker y migraciones de base de datos
Estado: implementación local disponible; adopción de producción pendiente de inventario y ensayo.
Fecha: 17 de octubre de 2026.
Operación vigente: [Base de datos local y migraciones](./desarrollo-y-migraciones-bd.md).
Este documento conserva la planificación y sus criterios de aceptación.
## Objetivo y alcance
Levantar MariaDB local para desarrollo y tests, y evolucionar el esquema mediante
migraciones versionadas. La referencia facilitada para producción es
`11.8.3-MariaDB-ubu2404`.
El backend y el frontend seguirán ejecutándose en WSL con Node.js 24 y PNPM
11.25.0. Esta adaptación no requiere contenerizar toda la aplicación ni cambiar
la base de producción. Su incorporación al historial se preparará y ensayará
antes de ejecutarse en ese entorno.
## Situación de partida
- Los E2E ya provisionan una MariaDB efímera con Compose, puerto dinámico,
credenciales generadas y limpieza al finalizar.
- Los E2E preparan el esquema mediante `sequelize.sync()`.
- Los tests HTTP crean bases con nombres únicos sobre un servidor configurado
y fuerzan `DB_SYNC_MODE=alter`; su cierre no elimina esas bases.
- El runtime permite `none`, `alter` y `force`, sin restricción por entorno.
- Los scripts SQL están repartidos entre `docs` y `modules/identity/sql`;
mezclan DDL, cambios de datos, seeds, diagnósticos y procedimientos históricos.
- No existe un historial versionado de migraciones.
- El build del servidor tiene un único punto de entrada. Las migraciones deben
formar parte del artefacto desplegable.
- El Dockerfile raíz declara PNPM 10.20.0, mientras `packageManager` exige
11.25.0. Habrá que alinear el build afectado sin modificar `packageManager`.
## Decisiones propuestas
- Usar la imagen oficial `mariadb:11.8.3`, verificar el servidor resultante y
registrar su digest para fijar el artefacto utilizado.
- Centralizar la referencia de imagen utilizada en desarrollo, HTTP, E2E y CI.
- Usar Compose para el ciclo de vida de MariaDB. Reutilizar la infraestructura
E2E existente donde encaje, sin crear un framework nuevo.
- Usar Umzug con Sequelize v6 para ejecutar migraciones TypeScript mediante un
catálogo explícito y ordenado. No depender de descubrir archivos TS en producción.
- Ubicar el ejecutor y el catálogo en `modules/core/src/api/infrastructure/database`
para compartir contrato entre servidor y worker. La CLI reside en
`apps/server/src/infrastructure/database`. No entran en Domain/Application.
- Mantener migraciones aplicadas inmutables. Los cambios posteriores se harán
mediante nuevas migraciones, sin importar modelos ORM que puedan evolucionar.
- Separar migraciones, datos de demostración y fixtures de tests. Los datos
funcionales indispensables se clasificarán expresamente antes de incorporarlos.
- Ejecutar migraciones con una orden explícita, separada del arranque del backend.
El backend comprobará compatibilidad sin modificar el esquema.
## Fase 0. Inventario y contrato inicial
1. Confirmar Docker/Compose disponibles desde WSL. Resolver la discrepancia entre
la ruta real del checkout y la indicada en `AGENTS.md` antes de modificar código.
2. Inventariar modelos registrados realmente por la composición del servidor,
asociaciones, índices, restricciones y tablas de auditoría.
3. Clasificar todos los SQL: vigente, histórico, seed, diagnóstico o limpieza.
Registrar dependencias y precondiciones, sin ejecutarlos en lote.
4. Comparar el esquema esperado con un inventario autorizado de desarrollo y,
cuando se disponga de él, de producción. No asumir que coinciden con los modelos.
5. Determinar `charset`, `collation`, `sql_mode`, zona horaria y configuración
relevante de producción. Mantener explícitamente cualquier diferencia necesaria.
6. Resolver diferencias detectadas entre SQL y modelos, por ejemplo `archived_at`
y la longitud de `proforma_number`, sin escoger un contrato arbitrariamente.
Salida: inventario y contrato de esquema revisables; lista de diferencias por
entorno y clasificación de tablas legacy. No se eliminan datos ni tablas.
## Fase 1. MariaDB de desarrollo
1. Añadir un Compose local dedicado a la base de datos, independiente de los
stacks de despliegue por empresa.
2. Usar un volumen nombrado persistente y publicar el puerto solo en `127.0.0.1`.
3. Incorporar `healthcheck.sh --connect --innodb_initialized` y comprobación SQL
antes de preparar el esquema.
4. Definir configuración de ejemplo sin secretos reales. Usar credenciales
distintas para administrar/migrar y para ejecutar la aplicación.
5. Añadir órdenes PNPM para levantar, detener y consultar logs. El reset de datos
será una orden separada, explícita y limitada al proyecto local correspondiente.
6. Documentar conexión desde WSL, persistencia y recuperación de un backup local.
Aceptación: detener y levantar conserva los datos; el reset solo afecta al
volumen local previsto; la imagen y la versión SQL son las esperadas.
## Fase 2. Ejecutor de migraciones
1. Incorporar Umzug como dependencia del servidor y un punto de entrada CLI
independiente, con configuración mínima de conexión y sin inicializar HTTP,
módulos, warmups, certificados ni servicios externos.
2. Definir identificadores ordenables, por ejemplo
`202610170001-initial-schema`, y un catálogo sin duplicados.
3. Persistir el historial de migraciones aplicadas y sus checksums. El historial
permitirá distinguir pendientes, aplicadas y cambios indebidos de archivos.
Usar el almacenamiento de Umzug como base y ampliar solo los datos necesarios.
4. Detectar historiales incompatibles: migraciones desconocidas, huecos y
checksums diferentes. Registrar inicio, resultado y fallos sin credenciales.
5. Impedir ejecuciones concurrentes mediante bloqueo de MariaDB mantenido en una
conexión dedicada durante toda la operación. Probar liberación y timeout.
6. Registrar una migración como aplicada únicamente después de completarla.
Los cambios con varios pasos tendrán precondiciones y recuperación documentada
ante ejecución parcial; no se reintentará a ciegas tras un fallo.
7. Incorporar órdenes propuestas `db:migrate`, `db:status` y `db:verify`.
`db:verify` comprobará historial y contrato de esquema; `db:status` será de lectura.
8. Compilar y empaquetar el ejecutor y todas las migraciones con el servidor;
comprobar su funcionamiento desde el artefacto final sin `tsx` ni fuentes TS.
MariaDB realiza commits implícitos en operaciones DDL. No se ofrecerá un rollback
transaccional general ni una orden destructiva de reversión de producción.
Se usarán transacciones para cambios de datos cuando proceda; para DDL, pasos
recuperables, migraciones correctivas y restauración verificada cuando sea necesaria.
Aceptación: una base vacía se prepara; una segunda ejecución no cambia nada;
un fallo no aparece como éxito; se detectan modificaciones del historial y se
impiden dos ejecutores simultáneos.
## Fase 3. Migración inicial y adopción de bases existentes
### Bases nuevas
Crear una migración inicial explícita con el esquema activo completo, validado
contra el inventario: identity, companies, catalogs, customers, suppliers,
customer-invoices, recurring-proformas, onboarding, factuges y auditoría, según
su registro real. No copiar automáticamente todos los modelos ni encadenar SQL legacy.
La migración inicial no incluirá empresas concretas, usuarios reales ni datos de
demostración. Los seeds de desarrollo serán opcionales e idempotentes.
### Bases existentes
1. Obtener backup verificable y ensayar el procedimiento en una copia aislada.
2. Ejecutar un diagnóstico de tablas, columnas, tipos, índices, restricciones y
datos relevantes para los cambios pendientes.
3. Preparar reconciliaciones explícitas para las diferencias demostradas.
Conservar tablas legacy y datos ajenos al esquema activo.
4. Permitir registrar la migración inicial como ya existente solo después de
verificar el contrato. Rechazar bases incompatibles; no marcar todas las
migraciones como aplicadas ni ejecutar el DDL inicial sobre ellas.
5. Registrar la evidencia de adopción y ejecutar después las migraciones pendientes.
Aceptación: una instalación nueva y una base existente reconciliada alcanzan el
mismo contrato activo. Los datos existentes y las tablas legacy se conservan.
## Fase 4. Tests sobre migraciones reales
1. Mantener el ciclo de vida Compose de los E2E y fijar la imagen de MariaDB.
2. Sustituir `sequelize.sync()` en la preparación E2E por el mismo ejecutor de
migraciones. Mantener separados bootstrap y datos de escenario.
3. Añadir un launcher de integración HTTP que provisione MariaDB temporal con
credenciales generadas. Mantener una base única por runtime cuando lo requiera
el aislamiento actual y eliminar todas al terminar el contenedor.
4. Sustituir `DB_SYNC_MODE=alter` del runtime HTTP por migraciones y `none`.
5. Limitar creación/eliminación de bases a la instancia temporal aprovisionada.
No aceptar automáticamente servidores externos desde `.env.test`.
6. Mantener los tests unitarios independientes de Docker cuando no necesiten BD.
7. Añadir pruebas de instalación vacía, reejecución, fallo parcial recuperable,
concurrencia, checksums y adopción de esquema compatible/incompatible.
8. Probar actualizaciones desde versiones anteriores con datos representativos,
incluyendo aislamiento por empresa, referencias, contadores e integridad fiscal.
Aceptación: HTTP y E2E pasan con el esquema creado por migraciones; dos ejecuciones
concurrentes no comparten datos; éxito, fallo y cancelación limpian sus recursos.
## Fase 5. Retirada de sincronización y despliegue
1. Tras convertir los consumidores, retirar `alter` y `force` del arranque normal
y actualizar configuración, tests y ejemplos. Rechazar explícitamente esas opciones.
2. Hacer que el arranque falle con un diagnóstico claro si el historial no es
compatible o quedan migraciones pendientes. No aplicar DDL automáticamente.
3. Añadir a CI instalación vacía, actualización desde versión anterior, verificación
del esquema y pruebas desde el artefacto compilado.
4. Preparar el procedimiento de despliegue: backup, control de escrituras cuando
proceda, ejecución única de migraciones, verificación y arranque de procesos.
5. Separar privilegios del usuario migrador y de la aplicación. Revisar los permisos
que requieran los warmups existentes antes de reducirlos.
6. Documentar compatibilidad entre versiones de aplicación/esquema y recuperación.
Ejecutar cambios destructivos solo tras auditoría y una migración posterior.
7. Marcar los SQL antiguos como referencia histórica o procedimiento específico,
enlazando la migración que los sustituye. Conservar los necesarios para auditoría.
## Entregas y validación
Entregas incrementales propuestas:
1. Inventario, contrato y Compose de desarrollo.
2. Ejecutor, historial, migración inicial y artefacto compilado.
3. Diagnóstico y adopción de bases existentes, ensayados en copias.
4. Integración HTTP y E2E mediante Docker y migraciones.
5. Retirada de sincronización, CI y procedimiento operativo.
No retirar la preparación anterior hasta que su sustitución funcione en la misma
entrega. Cada entrega tendrá sus comprobaciones dirigidas; al modificar contratos
compartidos se ejecutarán `pnpm typecheck` y `pnpm lint` globales, las suites
unitarias pertinentes, `pnpm --filter @erp/factuges-server test`, `pnpm test:e2e`
y `pnpm --filter @erp/factuges-server build`, según alcance. El ejecutor compilado
se verificará contra una MariaDB temporal. Se informarán fallos y comprobaciones
no ejecutadas; este documento no implica que hayan pasado.
## Datos pendientes para cerrar la implementación
- Configuración efectiva de producción y esquema activo real.
- Disponibilidad de una copia anonimizada o fixtures equivalentes para ensayar adopción.
- Cómo se ejecutan actualmente los despliegues y qué CI se utiliza.
- Recursos Docker disponibles para ajustar el paralelismo de tests.
Estas comprobaciones no impiden preparar la infraestructura local y el ejecutor,
pero sí condicionan el contrato final y la adopción de producción.
## Referencias
- [Imagen MariaDB 11.8.3 basada en Ubuntu Noble](https://hub.docker.com/layers/library/mariadb/11.8.3/).
- [Umzug: ejecución e historial con Sequelize](https://github.com/sequelize/umzug).
- [MariaDB: sentencias con commit implícito](https://mariadb.com/docs/server/reference/sql-statements/transactions/sql-statements-that-cause-an-implicit-commit).
- [Infraestructura E2E existente](../tests/e2e/README.md).