Dónde viven realmente las reglas
A través de 2.400 archivos .fmb que hemos analizado, los triggers WHEN-VALIDATE-ITEM representan el 38% de todo el PL/SQL embebido por número de líneas. Son el bloque individual más grande de lógica de negocio en la mayoría de las aplicaciones de Oracle Forms — más que todos los triggers a nivel de formulario, cuerpos de paquete y unidades de librería combinados. Una aseguradora con la que trabajamos tenía 9.700 de ellos en un sistema de administración de pólizas desplegado por primera vez en 2001.
Las migraciones triunfan o fracasan según cómo se traduzcan estos triggers.
Qué se supone que hace WHEN-VALIDATE-ITEM
El trigger se ejecuta cuando un item pierde el foco y su valor ha cambiado. Su función es decidir si el nuevo valor es aceptable. Si no lo es, lanza FORM_TRIGGER_FAILURE, que devuelve el foco al item y muestra un mensaje. Las reglas varían — verificaciones de rango, dependencias entre campos, consultas a la base de datos, límites regulatorios.
-- WHEN-VALIDATE-ITEM típico
IF :POLICY.COVERAGE_AMOUNT > 1000000
AND :POLICY.UNDERWRITER_LEVEL < 3 THEN
MESSAGE('Coverage above 1M requires senior underwriter');
RAISE FORM_TRIGGER_FAILURE;
END IF;
Ese ejemplo de cuatro líneas toca dos items de formulario, un umbral codificado y un mensaje de visualización. Es representativo. El trigger WHEN-VALIDATE-ITEM mediano en nuestra muestra tiene 11 líneas. El percentil 95 tiene 84.
El objetivo de la traducción
Nuestra forma objetivo es una función validadora pura en TypeScript que toma el estado actual del formulario y devuelve null o un objeto de error. Sin efectos secundarios, sin acceso al DOM, sin llamadas directas a la base de datos desde el propio validador.
export const validateCoverageAmount: Validator<PolicyForm> = (state) => {
if (state.coverageAmount > 1_000_000 && state.underwriterLevel < 3) {
return { field: "coverageAmount",
message: "Coverage above 1M requires senior underwriter" };
}
return null;
};
Las consultas a la base de datos se mueven a validadores asíncronos que llaman a un endpoint REST generado. El validador permanece puro; el endpoint es responsable del acceso a datos. Esta separación es la decisión más importante de la traducción — es lo que hace que las reglas sean testables, cacheables y auditables.
Manejando los casos complejos
No todos los triggers son cuatro líneas limpias. Hemos catalogado cinco patrones que resisten la traducción ingenua:
- Commits implícitos. Triggers que llaman a
COMMITa mitad de la validación. Estos se refactorizan en pasos de guardado explícitos. - Variables globales. Referencias a
:GLOBAL.xyzque almacenan estado de sesión entre formularios. Estas se convierten en un store de sesión tipado. - Llamadas DO_KEY. Triggers que re-disparan eventos de navegación. Estas se convierten en transiciones de estado explícitas.
- SQL dinámico. Llamadas a
FORMS_DDLyEXEC_SQL. Estas se marcan para revisión humana — aproximadamente el 6% de los triggers caen aquí. - Referencias entre formularios. Lectura de items de otro formulario abierto. Estas se convierten en objetos de contexto con alcance de sesión.
Aproximadamente el 91% de los triggers WHEN-VALIDATE-ITEM caen en patrones limpios que se traducen automáticamente. El 9% restante necesita revisión. Saber cuál es ese 9% antes de que comience el proyecto es la diferencia entre un cronograma predecible y un retraso trimestral.
Preservando la semántica que el compilador no ve
Las reglas más difíciles son las que dependen del modelo de ejecución de Oracle Forms. Un WHEN-VALIDATE-ITEM solo se dispara cuando el item ha cambiado — no en cada guardado, no en resultados de consulta, no en asignaciones programáticas. Equivocarse en esto significa que los validadores se disparan con demasiada frecuencia y rompen flujos de trabajo que antes funcionaban.
Reflejamos la semántica de Forms explícitamente. El runtime del validador rastrea el estado dirty por campo, suprime la validación durante la población de consultas y respeta la jerarquía original de triggers (item, bloque, formulario). La regla traducida es idéntica; el runtime que la invoca es lo que iguala el comportamiento.
Probando la traducción
Cada validador migrado recibe dos pruebas automáticamente: una generada a partir del flujo de control del PL/SQL original, y una capturada del tráfico de producción contra el sistema legacy. La segunda es más importante. Reproducimos seis meses de envíos reales de formularios a través de los validadores antiguos y nuevos y comparamos los resultados. Cualquier divergencia es un defecto.
En los últimos cuatro proyectos, esta reproducción detectó entre 14 y 71 defectos por cada 1.000 triggers — casi todos en los patrones de casos complejos mencionados anteriormente.
La conclusión
WHEN-VALIDATE-ITEM es donde viven 25 años de conocimiento institucional. Traducirlo a TypeScript no es un ejercicio sintáctico — es un ejercicio semántico, y la semántica depende del modelo de ejecución de Forms tanto como del propio código. Las migraciones que preservan las reglas de negocio limpiamente son las que separan la validación pura del acceso a datos, reflejan el modelo de ejecución original y reproducen tráfico real antes del corte.