La lógica que vive fuera de los formularios
Una empresa de trading energético nos pidió estimar una migración la primavera pasada. El inventario de .fmb resultó en 184 archivos y aproximadamente 340.000 líneas de PL/SQL incrustado. Alcance razonable. Luego ejecutamos el análisis de dependencias contra la base de datos. Los formularios referenciaban 412 paquetes PL/SQL, 1.840 triggers de base de datos y 96 variables de estado de paquete compartidas entre sesiones. El lado de la base de datos contenía 2,7 veces más código que los formularios mismos.
Esa proporción es típica. Los formularios son la punta. El iceberg está en la base de datos.
Los triggers de base de datos no son triggers de formularios
Los triggers de formularios se ejecutan en respuesta a eventos de interfaz. Los triggers de base de datos se ejecutan en respuesta a DML — INSERT, UPDATE, DELETE — y corren dentro de la transacción que emitió la sentencia. Aplican reglas referenciales, populan columnas de auditoría, realizan eliminaciones en cascada y ejecutan lógica de negocio que los formularios asumen que ocurrirá automáticamente.
Un trigger de auditoría representativo:
CREATE OR REPLACE TRIGGER orders_audit_trg
AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW
BEGIN
INSERT INTO orders_audit (order_id, action, changed_by, changed_at,
old_amount, new_amount)
VALUES (COALESCE(:NEW.order_id, :OLD.order_id),
CASE WHEN INSERTING THEN 'I'
WHEN UPDATING THEN 'U' ELSE 'D' END,
USER, SYSDATE, :OLD.amount, :NEW.amount);
END;
Las migraciones que pasan a una nueva capa de aplicación a menudo asumen que pueden reemplazar esto con registro de auditoría a nivel de aplicación. Esa suposición se rompe en el momento en que un trabajo por lotes, un script SQL*Plus o un generador de reportes modifica la misma tabla. El trigger de base de datos capturaba todos los escritores. El registrador de aplicación captura uno.
El estado de paquete es un contrato oculto
Los paquetes PL/SQL de Oracle pueden mantener estado con alcance de sesión: variables declaradas a nivel de paquete que persisten entre llamadas dentro de la misma sesión de base de datos. Las aplicaciones de Forms usan esto intensamente — un usuario inicia sesión, la sesión dispara un procedimiento de login, y 40 variables de paquete se populan con ID de usuario, rol, centro de costo, calendario fiscal y límites de aprobación. Cada llamada subsiguiente del formulario lee de ese estado sin recargarlo.
Hemos contado entre 20 y 180 variables de estado de paquete por aplicación Forms. La mediana es 58. Ninguna de ellas es visible en los archivos .fmb. Ninguna de ellas sobrevive a una arquitectura REST sin estado sin remodelado explícito.
Las cuatro trampas que vemos repetidamente
En 14 proyectos de migración, los mismos cuatro problemas representan la mayoría de los retrasos en el cronograma del lado de la base de datos:
- Puntos de commit invisibles. Forms emite commits implícitos en la navegación de bloques. Los triggers de base de datos asumen que esos commits ocurren. Los endpoints REST que agrupan múltiples operaciones en una transacción rompen las suposiciones de los triggers.
- Deriva del estado de sesión. Las variables de paquete populadas al iniciar sesión se vuelven obsoletas cuando la nueva arquitectura usa pool de conexiones de base de datos. La misma conexión sirve a diferentes usuarios entre solicitudes.
- Cascadas de triggers. Un UPDATE dispara un trigger que emite otro UPDATE que dispara otro trigger. La profundidad de cascada en nuestra muestra alcanza 7. Los reemplazos a nivel de aplicación omiten pasos intermedios.
- Fugas de REF cursor. Los procedimientos almacenados devuelven REF cursors que los formularios consumen fila por fila. Un endpoint REST tiene que materializar el conjunto de resultados completo, lo que cambia las características de memoria y a veces dispara ORA-04030 en consultas grandes.
Cada uno tiene solución. Ninguno es obvio leyendo solo los archivos .fmb.
Lo que el análisis de dependencias debe capturar
Antes de escribir una línea de código de migración, extraemos el grafo completo de dependencias: cada tabla, vista, paquete, procedimiento, función, trigger y secuencia que los formularios tocan, más todo lo que esos objetos tocan a su vez. El grafo para una aplicación mediana tiene de 4.000 a 12.000 nodos.
El grafo es lo que nos indica si el estado de paquete puede reemplazarse con un almacén de sesión tipado, si los triggers de base de datos pueden dejarse en su lugar, o si la lógica de auditoría tiene que reescribirse en la capa de aplicación. Omitir este paso es el error más costoso en la migración de Oracle Forms. Lo hemos visto agregar de seis a nueve meses a proyectos que parecían limpios el primer día.
Dejar los triggers de base de datos en su lugar
Nuestra recomendación predeterminada es mantener los triggers de base de datos funcionando durante y después de la migración. Están probados en batalla, capturan escritores que no son la aplicación, y ya están en la matriz de controles de los auditores. La nueva aplicación TypeScript llama a procedimientos almacenados para transacciones complejas en lugar de reimplementar la lógica.
Esto no siempre es posible — algunos triggers llaman a DBMS_ALERT, UTL_HTTP u otras características que deben reescribirse — pero cuando lo es, elimina toda una clase de riesgo de migración. La base de datos sigue aplicando lo que siempre ha aplicado. La capa de aplicación cambia a su alrededor.
La conclusión
Las migraciones de Oracle Forms son migraciones de base de datos disfrazadas de interfaz de usuario. Los archivos .fmb son la superficie visible; el contrato real son los paquetes PL/SQL, los triggers de base de datos y el estado de paquete en los que los formularios se han apoyado durante décadas. Cada proyecto que entregamos a tiempo comenzó con un análisis completo de dependencias del lado de la base de datos, antes de que alguien abriera un formulario. Cada proyecto que se retrasó omitió ese paso.