FactuGES_ERP/docs/desarrollo-y-migraciones-bd.md

203 lines
10 KiB
Markdown

# Base de datos local y migraciones
## Entornos
[Resultados y límites de validación](./validacion-docker-y-migraciones-bd.md).
Desarrollo, integración HTTP y E2E utilizan la imagen oficial MariaDB 11.8.3
basada en Ubuntu 24.04, fijada por digest en
`infrastructure/database/compose.yaml`. Cambiar esa referencia requiere comprobar
compatibilidad con producción y ejecutar las suites de BD.
El código de la aplicación sigue ejecutándose en WSL Ubuntu-24.04, con Node.js 24
y PNPM 11.25.0. El checkout de esta adaptación está en
`/home/david/dev/factuges/factuges-erp`; la ruta anterior documentada en AGENTS.md
no existe en este entorno. Ejecutar los comandos desde la raíz Linux real.
## Desarrollo
```bash
pnpm db:dev:up
pnpm db:migrate
pnpm db:status
pnpm db:verify
pnpm dev:server:local
```
La primera orden genera `infrastructure/database/.env` si falta, con contraseñas
aleatorias y permisos 0600. No sobrescribe ficheros existentes. El archivo está
excluido de Git; no compartirlo ni copiar sus valores en documentación o logs.
Compose publica MariaDB en `127.0.0.1:3307`, usa la base
`factuges_erp_development` y conserva los datos en un volumen nombrado. El puerto
se puede cambiar en ese `.env`. Las órdenes de desarrollo leen exclusivamente
ese fichero y rechazan otros nombres de base u hosts.
Los comandos `dev` del servidor y `dev:server:local` leen el `.env` habitual
de `apps/server` (si existe) y superponen únicamente
la conexión Docker local, sin pasarle al proceso la contraseña administrativa ni reescribir
archivos existentes. Para arrancar el servidor con otras herramientas, configurar
la conexión local con `DB_HOST`, `DB_PORT`, `DB_NAME`,
`DB_USER` y `DB_PASS` del archivo generado, y `DB_SYNC_MODE=none`. El `.env` de la
aplicación necesita también su configuración habitual de HTTP, JWT y servicios.
No se reescriben los `.env` locales existentes automáticamente.
El usuario de aplicación tiene lectura del esquema y del historial, y DML por
tabla activa. No puede escribir en `database_migrations`. Las órdenes locales de migración
usan la contraseña administrativa en una conexión separada. La inicialización del volumen concede lectura; `db:migrate`, `db:adopt` y
`db:recover` ajustan los permisos del usuario local después de verificar el
esquema. Modificar las credenciales de un `.env` no cambia cuentas ya persistidas.
```bash
pnpm db:dev:logs
pnpm db:dev:down
```
Detener Compose conserva el volumen. Para eliminar exclusivamente los datos de
este proyecto local existe una orden distinta:
```bash
pnpm db:dev:reset --confirm-reset
```
El reset no forma parte del arranque ni de las migraciones. Después hay que volver
a levantar la base y ejecutar `db:migrate`. No se cargan empresas ni cuentas de
demostración automáticamente; se conserva el flujo funcional de bootstrap/onboarding.
## Migraciones
El catálogo y el motor están en
`modules/core/src/api/infrastructure/database`, compartidos por servidor y worker.
Esta ubicación evita dependencias entre aplicaciones. La CLI está en
`apps/server/src/infrastructure/database` y no necesita HTTP, JWT ni certificados.
Se utiliza Umzug sobre una conexión SQL dedicada de `mysql2`. Se mantiene la misma
sesión desde `GET_LOCK` hasta `RELEASE_LOCK`, incluidos los commits implícitos del
DDL. La aplicación continúa utilizando Sequelize v6.
La migración `202610170001-initial-schema` crea las 30 tablas del runtime activo.
Es SQL congelado, ordenado por dependencias y sin imports de modelos. Las longitudes,
fechas, índices, claves externas y colaciones reflejan los modelos registrados al
preparar esta versión; no se han cambiado contratos de dominio para acomodar SQL antiguo.
`database_migrations` registra nombre, checksum SHA-256, estado, fechas y si se
adoptó un esquema existente. La aplicación no tiene permisos para modificar ese
historial en desarrollo. Un checksum distinto, una versión desconocida, un hueco
o una migración incompleta impiden continuar.
Servidor y worker verifican historial y definiciones de tablas antes de arrancar.
La comparación es deliberadamente estricta: usa `SHOW CREATE TABLE`, ignora el
contador `AUTO_INCREMENT` y el formato de líneas, pero no diferencias en columnas,
índices, restricciones, orden o nombres. Tablas adicionales ajenas al contrato,
por ejemplo legacy, se conservan y no impiden adoptar una base compatible.
`DB_SYNC_MODE` solo admite `none`. No se ejecuta `sequelize.sync()` al arrancar,
preparar HTTP ni preparar E2E.
### Incorporar un cambio nuevo
1. Añadir un fichero de migración con SQL explícito y un identificador ordenable
único al catálogo. No modificar migraciones ya aplicadas ni importar modelos.
2. Actualizar el contrato vigente en `schema-contract.ts`, conservando el snapshot
inicial. Los cambios del esquema y los datos deben respetar tenants y referencias.
3. Comprobar precondiciones y prever ejecución parcial. Una segunda invocación del
ejecutor debe dejar aplicadas las migraciones sin repetirlas.
4. Añadir una prueba de actualización desde la versión anterior con datos representativos,
además de la instalación vacía y las verificaciones de contrato.
5. Ejecutar las suites dirigidas, build y comprobaciones del área afectada.
Los seeds de escenarios pertenecen a los tests; los datos opcionales de desarrollo
no son migraciones. No reutilizar UUIDs de empresas de los antiguos SQL de ejemplo.
## Bases existentes
No ejecutar la migración inicial sobre una base que ya contiene tablas. El comando
`migrate` lo rechaza antes de aplicar DDL.
1. Obtener un backup y comprobar su restauración en una instancia aislada.
2. Usar `status` para ver tablas ausentes o diferentes. Comparar sus definiciones
con el SQL inicial y revisar los datos antes de cualquier reconciliación.
3. Preparar cambios específicos para las diferencias reales. La herramienta no
elimina columnas, tablas legacy ni datos automáticamente.
4. Cuando el esquema activo coincida, ejecutar `adopt` con el ejecutable de esta
versión inicial. Solo registra el baseline; no recrea tablas ni modifica sus datos.
5. Ejecutar `verify` y ensayar los flujos funcionales antes del cambio real.
La adopción inicial se limita a esta versión de baseline. Si ya existen migraciones
posteriores, usar las herramientas del baseline para adoptar y después actualizar
con el ejecutor de la versión destino. No marcar todas las versiones como aplicadas.
La herramienta verifica estructura, no garantiza por sí sola la validez de todos
los datos históricos. Deben auditarse huérfanos, contadores y contratos fiscales en
la copia del entorno concreto. No se ha inspeccionado ni adoptado producción.
## Fallos y recuperación
MariaDB hace commits implícitos con DDL: una migración de varios pasos puede quedar
parcialmente aplicada. El ejecutor registra `running` antes del SQL, `failed` si
puede registrar el fallo, y `applied` únicamente tras completarla. Un proceso
interrumpido puede dejar `running`; nunca se reintenta automáticamente ese SQL.
`status` es de lectura y muestra estados incompletos y diferencias. Corregir o
restaurar la base mediante un procedimiento revisado. `recover` solo permite
registrar como aplicada una migración final incompleta cuando el esquema completo
coincide con el contrato y no hay migraciones posteriores pendientes. No ejecuta
SQL correctivo ni comprueba backfills de datos: estos deben comprobarse por separado
antes de recuperar. No se ofrece rollback destructivo automático.
## Tests y CI
```bash
pnpm test:unit
pnpm test:db
pnpm test:http
pnpm test:e2e
```
Cada launcher crea su propio proyecto Compose con nombre aleatorio, puerto local
dinámico y credenciales generadas. No utiliza la MariaDB de desarrollo ni la de
producción. El contenedor y sus volúmenes se eliminan al terminar, también ante
fallos. `test:unit` excluye las suites HTTP y los ficheros `*.integration.test.ts`;
no necesita Docker. El paralelismo se limita a cuatro procesos para evitar
crear cientos de workers simultáneos.
La suite HTTP usa una base con nombre único por runtime sobre su instancia
exclusiva. La contraseña administrativa se transmite solo al runtime de test para
crear esas bases y conceder acceso al usuario temporal. No toma su destino de
`.env.test`: el launcher lo sustituye y el runtime verifica su prefijo de ejecución.
La configuración no relacionada con BD puede proceder de `.env.test` o, si falta,
de `.env.test.example`.
Los E2E conservan sus ficheros locales de configuración de aplicación y sus fixtures;
la preparación del esquema ahora ejecuta las mismas migraciones que desarrollo.
Ver [infraestructura E2E](../tests/e2e/README.md).
La suite de migraciones comprueba instalación vacía, reejecución conservando datos,
adopción compatible/incompatible, checksum, diferencias de esquema, bloqueo y
recuperación. También comprueba la CLI compilada. `pnpm test:database:ci` agrupa
las validaciones de base de datos; requiere Docker y los `.env.e2e` configurados.
Puede integrarse en el proveedor de CI existente sin imponer uno nuevo.
## Ejecución desde el artefacto desplegable
```bash
pnpm --filter @erp/factuges-server build
node --env-file=/ruta/segura/migraciones.env apps/server/dist/database-cli.js status
node --env-file=/ruta/segura/migraciones.env apps/server/dist/database-cli.js migrate
node --env-file=/ruta/segura/migraciones.env apps/server/dist/database-cli.js verify
```
En un contenedor de aplicación se ejecuta la misma CLI incluida en `dist` como
proceso independiente. Su configuración mínima es `DB_HOST`, `DB_PORT`, `DB_NAME`,
`DB_USER`, `DB_PASS` y, si corresponde, `DB_SSL=true`. Para producción, suministrar
credenciales de migración separadas y verificar el backup y la compatibilidad de
aplicación antes del despliegue. El usuario ordinario requiere DML y lectura de
metadatos/historial; revisar cualquier permiso adicional que necesite la instalación.
Las órdenes PNPM de la raíz `db:*` están restringidas a desarrollo. Para otros
destinos usar la CLI compilada con un archivo de configuración explícito.
Los antiguos SQL en `docs/*/sql` y `modules/identity/sql` quedan como referencia
histórica, diagnóstico o procedimiento específico. No se ejecutan automáticamente
ni constituyen una segunda fuente del esquema vigente.