16 KiB
Normalización de la persistencia
Se mantiene Sequelize 6 y MariaDB. El criterio del proyecto es almacenar datos de negocio en columnas y relaciones, sin documentos JSON. Los objetos values del dominio y el JSON del contrato HTTP no implican columnas JSON.
Estado de la normalización
La auditoría y normalización de la persistencia actual están completadas. Activos, recursos, configuraciones, tickets e intervenciones se completan con cuentas y accesos, correo, accesos remotos, procesos y sesiones HTTP. No se mantienen documentos JSON en la base de desarrollo; los contratos HTTP continúan usando JSON como transporte.
| Ámbito | Persistencia actual |
|---|---|
| Compañías, personas, departamentos, sedes, usuarios y marcas | Columnas y relaciones existentes |
| Catálogo e inventario de activos | Definiciones, campos tipados, especificaciones y revisiones normalizados previamente |
| Catálogo e inventario de recursos | Definiciones, opciones, secciones, especificaciones congeladas, valores tipados e integración en columnas/tablas |
| Puestos de trabajo | Nombre, estado y referencias en columnas; activos exclusivos/compartidos por puesto y revisión en tablas |
| Asistencia técnica de activos | Destino, VPN, preferencia, estado y notas en columnas actuales e históricas |
| Perfiles de usuario | Nombre, estado, notas y referencias en columnas actuales e históricas |
| Teletrabajo | Estado, periodo, notas y persona en columnas actuales e históricas |
| Aplicaciones, certificados, impresoras y carpetas del perfil | Valores escalares y referencias en columnas actuales e históricas |
| Cuentas y accesos | Subtipos en tablas actuales e históricas; permisos de necesidades en asociaciones ordenadas |
| Correo | Buzones, usos y configuraciones en columnas actuales e históricas; variantes legadas conservadas |
| Accesos remotos | Origen, destinos con claves foráneas independientes, VPN y estado en columnas actuales e históricas |
| Procesos y plantillas de procesos | Columnas escalares y tablas de instrucciones/tareas actuales e históricas |
| Tickets e intervenciones | Columnas actuales e históricas; afectados, pasos, procedimientos congelados y plantillas en tablas relacionadas |
| Sesiones HTTP | Almacén Sequelize propio: autenticación, CSRF y opciones de cookie en columnas; sin documento serializado |
Las columnas data de fotos y logotipos son datos binarios; no son documentos JSON de negocio.
Recursos
Las tablas existentes resource_definitions, resource_template_fields, resources y resource_revisions mantienen sus identidades. Se añaden:
resource_field_choices: opciones ordenadas de cada campo.resource_template_sections: secciones ordenadas, identificadas por(templateId, id); copiar una plantilla puede conservar los identificadores internos de sus secciones.resource_specification_sections,resource_specification_fieldsyresource_specification_field_choices: definición congelada al crear el recurso.resource_characteristicsyresource_revision_characteristics: valores actuales e históricos con columnas independientes de texto, número, booleano y fecha.
La ausencia de una característica se representa mediante ausencia de fila. Una cadena vacía, 0 y false siguen siendo valores y se conservan. Las fechas se leen como fechas ISO. La especificación congelada no se reemplaza por el estado actual del catálogo.
Los vínculos de integración con buzones y accesos se conservan en columnas con claves foráneas. Eliminar una referencia externa no debe eliminar en cascada el recurso ni sus revisiones. Las proyecciones de compatibilidad de correo/accesos conservan las integraciones y sus identidades; sus lectores y escritores también utilizan columnas y tablas normalizadas.
Puestos, perfiles y configuraciones
workstation_members conserva la composición actual del puesto, con tipo exclusive o shared y posición. workstation_revision_members conserva la composición de cada revisión. workstation_assets sigue siendo la proyección técnica que protege referencias históricas y reserva activos exclusivos de puestos vigentes; no es otra asignación de negocio.
Los perfiles y configuraciones mantienen sus reglas de unicidad, transiciones y cierre. El cambio de persistencia no modifica rutas, formularios, DTO, contadores, permisos, identidad ni comportamiento funcional. Las referencias de las revisiones se conservan mediante claves foráneas.
Tickets, intervenciones y plantillas
Los seis registros principales (tickets, ticket_revisions, interventions, intervention_revisions, intervention_templates, intervention_template_revisions) ya no almacenan values en JSON. Asunto, descripción, estados, prioridad, canal, modalidad, fechas, minutos, diagnóstico, trabajo realizado, resolución, nombre y estado de la plantilla tienen columnas tradicionales según la entidad. plannedDate es una fecha SQL; los instantes ISO conservan su representación exacta en columnas de texto para no perder precisión del historial.
Se añaden diez tablas:
| Información | Actual | Histórica |
|---|---|---|
| Afectados de tickets | ticket_targets |
ticket_revision_targets |
| Afectados de intervenciones | intervention_targets |
intervention_revision_targets |
| Ejecución de pasos | intervention_steps |
intervention_revision_steps |
| Pasos de plantillas | intervention_template_steps |
intervention_template_revision_steps |
| Procedimiento congelado | intervention_procedures |
Procedencia inmutable de la intervención |
| Pasos originales del procedimiento | intervention_procedure_steps |
Copia independiente del catálogo |
Un afectado tiene exactamente una referencia: persona, puesto, activo o recurso, con clave foránea real para cada tipo. Las asociaciones se identifican por propietario y posición; los pasos mantienen su UUID y un orden único dentro de cada propietario. Las claves foráneas restringen eliminar destinos referenciados, mientras que las filas dependientes pertenecen a su ticket, revisión, plantilla o procedimiento y se eliminan en cascada con su propietario.
El procedimiento conserva plantilla, revisión concreta, nombre, descripción, estado original y pasos. La base comprueba que la revisión pertenece a esa plantilla. No se sustituye esa copia por el catálogo vigente. Una intervención sencilla puede no tener procedimiento, pasos, técnico o ticket, según las reglas existentes.
Los repositorios reconstruyen los mismos Value Objects y agregados. Se mantienen las rutas, DTO, UUID, autores, fechas, contadores, reintentos idempotentes, revisión esperada, conflictos concurrentes y reglas para cerrar o reabrir. Los lectores del dashboard consultan las columnas normalizadas tanto del estado actual como de las revisiones.
Migraciones
Las migraciones se ejecutan explícitamente al inicializar los módulos; no se utiliza sync({ alter: true }).
- Se añaden columnas y tablas de destino sin borrar el origen.
- Se valida el documento anterior mediante el esquema de dominio, incluidas las referencias ya persistidas.
- La transferencia de valores se realiza en una transacción.
- Se reconstruye el destino y se compara con el origen antes de retirar las columnas antiguas.
- Se eliminan las columnas JSON transferidas y se refuerzan las restricciones.
El DDL de MariaDB no es transaccional. Una interrupción puede dejar columnas de preparación, pero el origen se conserva hasta verificar la transferencia. Las migraciones de tickets, intervenciones y plantillas permiten reconstruir conjuntamente registros legados y normalizados si el DDL se interrumpe entre columnas. La migración de recursos admite también el corte en el que las revisiones ya se transfirieron y el recurso aún conserva sus columnas de origen. Repetir las migraciones no añade revisiones ni duplica datos.
Las pruebas de integración utilizan exclusivamente metis_desk_test. Comprueban origen inválido, conservación de UUID/historial/orden/valores, referencias incompatibles, idempotencia, arranque vacío y mantenimiento del comportamiento HTTP. Las fixtures SQL que antes escribían JSON se adaptan a las tablas nuevas.
Cuentas y accesos
Se conservan las raíces access_workspaces, access_items y access_revisions, y las proyecciones access_references y access_person_references. La raíz identifica compañía/ámbito y tipo; las revisiones mantienen UUID, secuencia, autor e instante. Cada tipo tiene una tabla de valores y otra de valores históricos:
| Tipo | Actual | Histórica |
|---|---|---|
| Sistema | access_system_values | access_system_revision_values |
| Tipo de recurso | access_resource_type_values | access_resource_type_revision_values |
| Permiso | access_permission_values | access_permission_revision_values |
| Plantilla de grupo | access_template_values | access_template_revision_values |
| Entorno | access_environment_values | access_environment_revision_values |
| Cuenta | access_account_values | access_account_revision_values |
| Titular/usuario de cuenta | access_account_person_values | access_account_person_revision_values |
| Grupo | access_group_values | access_group_revision_values |
| Pertenencia a grupo | access_membership_values | access_membership_revision_values |
| Proyección de recurso | access_resource_values | access_resource_revision_values |
| Permiso concedido | access_grant_values | access_grant_revision_values |
| Necesidad | access_need_values | access_need_revision_values |
| Cobertura | access_coverage_values | access_coverage_revision_values |
ownerId es clave primaria y foránea a la raíz o revisión correspondiente. Las referencias entre elementos y personas tienen claves foráneas. access_need_permissions y access_need_revision_permissions conservan permisos y orden; no permiten duplicar un permiso dentro de la misma necesidad. Las vistas access_current_values y access_historical_values unen columnas de los subtipos para lecturas comunes, sin generar JSON SQL.
El catálogo global y los ámbitos de compañía mantienen sus reglas. Se conservan titularidad histórica, cuentas compartidas, caducidad opcional, grupos, permisos, coberturas, bloqueos de concurrencia y proyecciones de referencias.
Correo y accesos remotos
mailboxes, mailbox_uses, mail_configurations y sus revisiones almacenan dirección, tipo/contexto, cuenta vinculada, personas autorizadas, periodo, perfil, protocolo, estado y notas en columnas. snapshotKind diferencia configuraciones históricas anteriores (persona/cuenta/activo) de las actuales (perfil/buzón). No se inventa un perfil para transformar el historial anterior. Las restricciones SQL exigen las referencias apropiadas a cada variante.
remote_connections y remote_connection_revisions mantienen persona, tipo de origen, activo corporativo cuando corresponde, sede/red, método, VPN, estado y notas. El destino se separa en targetAssetId y targetResourceId, con claves foráneas reales. Las restricciones SQL impiden destinos incompatibles, un origen personal con activo corporativo y acceso al propio equipo como destino.
Los repositorios, lectores de dashboard/procesos y proyecciones de recursos reconstruyen los mismos contratos anteriores. Se mantienen las configuraciones múltiples por persona, la adopción de configuraciones legadas y todo el historial.
Procesos
process_templates y process_template_revisions tienen columnas de nombre, tipo, estado y notas. Sus pasos ordenados se guardan en process_template_instructions y process_template_revision_instructions.
person_processes y person_process_revisions tienen persona, plantilla, nombre congelado de plantilla, nombre del proceso, tipo, estado y notas. person_process_tasks y person_process_revision_tasks conservan UUID de tarea, orden, título, estado, notas y referencia de revisión (tipo, UUID, etiqueta y procedencia).
La referencia de una tarea es información congelada para revisar una situación, no una asignación viva. No lleva FK al elemento revisado: debe conservarse aunque ese elemento se retire o elimine. Sí existen claves foráneas entre procesos, personas, plantillas, revisiones y sus tablas dependientes. Las instrucciones originales y tareas históricas no se reemplazan por una plantilla modificada.
Sesiones
http_sessions conserva sid, expiración y fechas de creación/actualización; añade columnas de usuario, versión de autenticación, token CSRF y opciones de cookie. La columna data desaparece. El almacén propio SequelizeSessionStore sustituye connect-session-sequelize, que se retira de las dependencias.
Se conservan duración original y expiración de la cookie, opciones booleanas, autenticación y protección CSRF. touch renueva la cookie sin sobrescribir autenticación o CSRF de otra petición. Las sesiones vencidas no se recuperan y se purgan al guardar sesiones; la expiración tiene índice. No se necesita temporizador de fondo. La migración rechaza atributos de sesión desconocidos en lugar de descartarlos.
Auditoría final
La auditoría de information_schema en desarrollo no encuentra tipos JSON, restricciones JSON_VALID ni columnas values, specification, characteristics, procedure u otros documentos serializados. Las columnas instructions de intervenciones son texto de instrucciones de un paso; data en fotos/logotipos es binario.
El código de producción no declara DataTypes.JSON. Los lectores JSON restantes corresponden a migraciones del origen anterior; las escrituras JSON de pruebas reconstruyen ese origen exclusivamente en metis_desk_test. El uso de JSON en HTTP, traducciones, logs y fixtures no es persistencia de documentos de negocio.
Verificación de la revisión anterior
- Suite completa: 277 pruebas superadas; espaciado: 3 pruebas superadas.
- Typecheck de servidor, activos, compañías y recursos: correcto.
- Build: correcto; mantiene el aviso de tamaño de bundles del frontend.
- Biome y ESLint sobre los ficheros modificados en esta revisión: correctos.
- Arranque y migraciones sobre una base de pruebas vacía: correctos.
- Inicialización de desarrollo: comprobada después de guardar una copia SQL externa al repositorio en
/home/david/backups/metis-desk/.
El typecheck global sigue encontrando el problema anterior de locale en packages/shadcn-ui/src/components/calendar.tsx. El lint/check global conserva errores de otros ficheros (principalmente código de fotos y formato pendiente). No se modifica código vendor ni cambios de interfaz ajenos a esta normalización.
Verificación de tickets e intervenciones
- Suite completa: 277 pruebas superadas, incluida la integración HTTP/MariaDB y la transferencia del formato legado; 3 pruebas de espaciado superadas.
- Typecheck de servidor y compañías, build, Biome y ESLint de todos los ficheros modificados: correctos.
- La prueba de integración reconstruye el origen JSON exclusivamente en
metis_desk_test, rechaza documentos y referencias incoherentes, conserva las instantáneas y contadores, comprueba restricciones de destinos y repite la migración después de simular un corte entre raíz y revisiones. También se ha ejercitado el arranque desde una base de pruebas vacía. - Base de desarrollo migrada e inicialización verificada. Auditoría del esquema: desaparecen las columnas
values/procedurede los seis registros principales y existen las diez tablas relacionadas. Copia previa:/home/david/backups/metis-desk/20261010-133639-tickets-intervenciones.sql. - Los chequeos globales mantienen el error previo de
localeen el calendario de shadcn/ui y errores de lint/formato en otros ficheros. No se modifica el código vendor ni cambios de interfaz realizados simultáneamente.
Verificación de cuentas, correo, accesos remotos, procesos y sesiones
- Suite completa: 277 pruebas superadas; 3 pruebas de espaciado superadas.
- Typecheck de servidor, identidad, compañías y recursos: correcto. Build: correcto.
- Biome y ESLint sobre todos los ficheros modificados: correctos, sin avisos.
- Integración HTTP/MariaDB desde una base de pruebas vacía: correcta. Se verifica transferencia del origen legado, rechazo de datos inválidos, conservación de valores actuales e históricos, UUID, orden, duración de cookies, CSRF, restricciones SQL e idempotencia, incluido un corte entre raíz e historial.
- Base de desarrollo migrada, inicialización verificada y auditoría de esquema sin columnas JSON ni documentos serializados. Copia previa: /home/david/backups/metis-desk/20261010-140951-normalizacion-restante.sql.
- El typecheck global mantiene el error previo de locale en el calendario vendor; lint/check global mantienen problemas de código y formato ajenos a estos cambios. No se modifica código vendor.