Trigger Frameworks – Triggers súper vitaminados (Parte 1)

Los triggers, son una funcionalidad ampliamente utilizada, pero que a medida que avanza el proyecto y se afianza, su uso desordenado provoca situaciones inadvertidas o efectos laterales indeseados, con frustración por parte del desarrollador y del usuario.

En este artículo quiero explicar, cuales son los problemas habituales, cuando surgen, y como se han desarrollado best practices y frameworks completos, que aportan soluciones, que con poco  esfuerzo permiten evitar estos situaciones complejas de diagnosticar.

Veamos pues, como crear Triggers vitaminados y super-mineralizados.

Este serie está formada por los siguientes artículos:

    1. Un Patrón Simple pero Potente: Análisis del patrón de Gestión de Triggers de Tony Scott, que introduce las mejores prácticas que todos los frameworks de triggers que fue publicado en la web de Salesforce Force.com Cookbook
    2. El Framework de Appleman que los inició a Todos: Análisis del Framework que publicó Dan Appleman en su libro Advanced Apex, y que es la referencia en muchos frameworks posteriores
    3. El Framework de Hari Krishman, que evolucionó los conceptos de los 2 anterires, y propuso una implementación mejorada de gran calidad

¿Por qué necesitamos Frameworks?

Los triggers son una capacidad fantástica en Salesforce, pero tienen varios problemas.

Existen varios problemas, que a medida que avanza el proyecto, se acentúan
  1. Orden no garantizado: varios desarrolladores pueden escribir una misma variante de trigger sobre un objeto, y Salesforce no garantiza el orden de su ejecución.
    1. Esto provoca, no asegurar como se realizará la ejecución y por tanto su resultado final.
    2. Tampoco permite, crear Tests fidefignos con resultados idempotentes.
    3. Incluso, podemos encontrar problemas en el entorno de Producción que nunca antes descubrimos en los entornos previos, es decir, problemas de interrupción de servicio.
  2. Recursividad incontrolada: el uso de triggers, conjuntamente con Workflows, procesos con Builder etc., crea un escenario tal que:
    1. ¿el trigger que estoy elaborando cómo cooperará con el resto de mecanismos existentes y que yo desconozco?
    2. ¿Cuándo me haya ido del proyecto, como mis compañeros desarrolladores o incluso administradores incorporarán nuevas funcionalidades que no interfieran en el código del trigger que estoy desarrollando?
    3. El máximo exponente de este problema, es la recursividad de actualizaciones DML: ejecuto un trigger que realiza una operación DML, que provoca la ejecución de otro trigger, que a su vez, provoca la ejecución de una workflow update, que provoca la ejecución nuevamente de mi trigger. Y se acaba de liar, porque los resultados son totalmente impredecibles, y lo peor de todo, yo no sé como ha pasado todo esto.
  3. Superación de Límites inadvertidamente: el escenario descrito en el punto 2, se ejecuta dentro del mismo contexto de ejecución, lo que dependiendo del número de operaciones DML o CPU utilizadas, provoca superar los límites, cuando inicialmente mi trigger, solo realizaba un número muy acotado de operaciones.
  4. Comprensión del entorno y refactoring complejo: refactorizar o cambiar la funcionalidad de nuestro trigger puede ser trivial, pero:
    1. ¿qué pasa en un escenario donde son varios los desarrolladores que están trabajando, cada uno con su funcionalidad pero que estamos trabajando con los mismos objetos?
    2. Y ¿qué pasa con los triggers, que otros compañeros que ya no están en el proyecto? ¿Y también con administradores o usuarios avanzados que modifican WFs o Processos?
    3. Cualquier cambio parece complejo y provoca incertidumbre.
    4. En el caso, de desarrollar Packages, es imposible saber a priori, que existirá en la ORG destino, con lo que aún es más impredecible
  5. In-habilitación dinámica de un trigger con errores: si tenemos el infortunio de llegar al entorno productivo con un trigger con Bugs, ¿cómo podemos interrumpir la deshabilitar ese trigger problemático y así darnos margen a corregirlo mientras los usuarios siguen trabajando?

…vale, pero que me ofrecerán

Los diversos frameworks existentes, tienen unos principios/objetivos que se centran en minimizar las debilidades comentadas y además en proporcionar nuevas capacidades.

A continuación, enumero estos principios y objetivos que persiguen todos los frameworks:

  1. Crear un único trigger por objeto y evitar duplicidades
  2. Crear el trigger sin código, externalizando todo su código en clases externas
  3. Controlar el orden de ejecución
  4. Centralizar el enrutamiento de la ejecución en función de los eventos
  5. Detección la recursividad / Control de re-entradas
  6. Activar/Desactivar un trigger en caliente en cualquier entorno
  7. Crear una estructura de código  que facilite un entorno multi-programador

Frameworks analizados

Han ido apareciendo propuestas e ideas sobre frameworks, que a cada uno de nosotros nos puede aportar cierto valor.

Es por ello que voy a centrarme en el análisis de las ideas expuestas en los frameworks, que más influencia tienen y cómo han ido aportando nuevas ideas y mejorando las de sus predecesores.

Los frameworks en los que me me basaré son:

  1. El artículo de Kevin O’hara en la Salesforce Developer Technical Library que servirá para establecer los conceptos básicos para un Framework de Triggers
  2. Trigger Architecture por Dan Appleman en su libro Advanced Apex Programming
  3. La propuesta de Hari Krishnan sobre una arquitectura de Triggers, que aglutina las ideas de las 2 propuestas anteriores más ideas procedentes de Tony Scott
  4. Finalmente, la propuesta de Sirono de la mano de Scott Wells (creador de Illuminated Cloud)
Trigger Frameworks de referencia que utilizaré para exponer las buenas prácticas e ideas

Principios básicos de un Framework para Triggers

Los principios básicos que la mayoría de desarrolladores y por supuesto los frameworks aplican son los 3 siguientes.

Un trigger por objeto

Desarrollar un único trigger por objeto. Como sabemos, desarrollar varios triggers para un mismo objeto, provoca, desconocer cual será el orden de ejecución, y por tanto desconocer como será la ejecución a cada invocación. ya que Salesforce no garantiza ningún orden.

Por ello y como principio súper básico, debemos centralizar en un único trigger, todas las posibles acciones, y no repetir su creación:

Un único Trigger por Objeto

Pero pero pero, en  ningún caso hay que dejar esta implementación así.

Si la dejamos así, cuando, por ejemplo realizamos una migración masiva de datos, esta se verá penalizada, incluso usando la BULK API, porque el trigger estará activo en cada operación que realicemos sobre el objeto. Es por eso que deberemos implementar un mecanismo de desactivación de triggers.

Trigger sin cuerpo, externalizando todo el código Apex en clases externas

Otro principio básico es extraer todo el cuerpo Apex de nuestro trigger hacia clases externas. Esto posee varios beneficios inmediatos:

  1. Mientras que varios desarrolladores pueden trabajar en distintas clases con un tasa de colisión baja, varios desarrolladores trabajando en un único trigger, requerirá de operaciones de Merge y seguramente provocará pérdidas de código y tiempo.
  2. Si exponemos la funcionalidad en clases externas, las podemos exponer para ser consumidas en nuestros Tests, mientras que si tenemos el código en un Trigger, este se vuelve complejo de testear.
  3. Como principio de orientación a objetos, exponer funcionalidad en clases, nos permite su reutilización, mientras que dejarla en el Trigger lo dificulta.

Añadir Handlers específicos por acción

Aunque inicialmente, necesitamos únicamente implementar una única operación, por ejemplo responder a un update, la generación de un esqueleto completo de operaciones, nos permite no modificar el código del trigger a posteriori, cuando aparezcan nuevas operaciones, y únicamente crear la nueva funcionalidad.

Para los desarrolladores noveles, este enfoque puede parecer un sobre-esfuerzo, y lo admito que lo es, pero a medida que el proyecto va creciendo, obtendremos el beneficio.

De hecho, veremos que seguir avanzando en esta idea, nos comportará crear interfaces y/o clases específicas que ampliarán este concepto proporcionándonos más capacidades y más versatilidad de gestión.

Aplicando los 2 conceptos anteriores, podemos implementar un esqueleto de un trigger como sigue (lo mejoraremos muchísimo, pero para mostrar las ideas creo que es ilustrativo):

Invocación a la Clase que gestionará todo el código de todos los triggers sobre Opportunity

Conclusiones

A estas alturas hemos solucionado 2 de las problemáticas que habíamos detectado, y realmente casi no hemos arañado la superficie de lo que llegaremos a conseguir mediante la ideas que implementan los autores de los frameworks mencionados.

Espero haber suscitado tu interés por la mejora del diseño e implementación de tus triggers, y prometo ampliarlo de manera creciente en las próximas entradas.

Enlaces Interesantes

  1. Apex Code Best Practices – Salesforce Documentation by Andrew Albert
  2. Trigger Frameworks and Apex Trigger Best Practices – By Kevin O’Hara
  3. Trigger Best Practices Discussion Forum
  4. Avoid Recursive Trigger Calls – Salesforce Knowledge
  5. Apex Trigger Framework – By Dan Appleman
  6. Trigger Pattern for Tidy, Streamlined, Bulkified Triggers Revisited – By Tony Scott
  7. An architecture framework to handle triggers in the Force.com platform – By Hari Krishnan
  8. Trigger handler framework – By Scott Wells on Sirono
  9. !Centralized Trigger Framework – By Scott Covert

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.