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

10 KiB

Base de datos local y migraciones

Entornos

Resultados y límites de validación.

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

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.

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:

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

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.

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

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.