¿A qué te refieres con "Dirigido a Eventos"?

1.776 palabras · 7 minutos

11/02/2017

Estás leyendo la traducción al castellano que he hecho del artículo 'What do you mean by "Event-Driven"?', escrito originalmente por Martin Fowler. Puedes acceder al original pulsando en este enlace.

Aproximadamente a finales del año pasado acudí con mis compañeros de ThoughtWorks a un seminario en el que pudimos discutir sobre la naturaleza de las aplicaciones "orientadas a eventos". A lo largo de los últimos años hemos construido muchos sistemas que hacen uso de este tipo de eventos, los cuales unas veces son alabados y, otras, maldecidos. Nuestra oficina de América del norte organizó un congreso al que acudimos desarrolladores senior de ThoughtWorks de varias partes del mundo para compartir nuestras ideas.

El resultado principal del seminario fue identificar a qué se refiere la gente cuando habla sobre "eventos" ya que este concepto, actualmente, significa cosas muy diferentes. De este modo pasamos mucho tiempo tratando de desenredar patrones que pudieran resultar útiles. Esta entrada es un breve resumen de los principales que identificamos.

Notificación de Eventos Event Notification

Ocurre cuando un sistema envía mensajes a través de eventos para informar a otros sistemas que se ha producido un cambio en su dominio. Un elemento clave de la notificación de eventos es que al sistema originario del mensaje no le importa mucho la respuesta. De hecho, a menudo, no espera ninguna respuesta en absoluto, o si hay una respuesta de la cual el origen se deba enterar, ésta le llegará de forma indirecta. De este modo hay una separación muy clara entre la lógica que sigue flujo que se encarga de enviar el evento y la lógica que sigue flujo que reacciona ante la respuesta de este evento.

La notificación de eventos es genial porque implica un nivel bajo de acoplamiento y es muy sencilla de llevar a cabo. Puede ser problemática, sin embargo, si realmente existe un flujo con lógica que se solape con otras notificaciones de eventos. El problema es que puede ser difícil ver cómo se desarrolla el flujo en el sistema ya que no está expresado de forma explícita en el código del programa. A menudo la única forma de averiguar cómo funciona todo es monitorizando un sistema en funcionamiento, lo que puede hacer que sea difícil de depurar y de modificar. El peligro radica en que es muy fácil hacer sistemas perfectamente desacoplados mediante notificación de eventos sin darnos cuenta de que estamos perdiendo de vista cómo funciona el flujo del sistema al completo, lo cual puede ocasionarte un problema en los próximos años. Aún así este patrón es muy útil, pero hay que tener cuidado de no caer en ninguna trampa.

Un ejemplo sencillo de trampa aparece cuando un evento se lanza en forma de orden pasivo-agresiva, lo cual ocurre cuando el sistema originario del mensaje espera que el receptor lleve a cabo una acción. Realmente utiliza el mensaje a modo de orden, aunque aparentemente es un evento.

Un evento no necesita incluir muchos datos en su interior, a menudo sólo un identificador único y la forma en la que obtener más información desde el origen. El receptor sabe que algo ha cambiado, quizá haya recibido alguna información mínima sobre la naturaleza del cambio, y quizá envíe una petición de vuelta al emisor para decidir qué hacer a continuación.

Transferencia del Estado en el Evento Event-Carried State Transfer

Este patrón aparece cuando se quiere actualizar información en un sistema de tal forma que no sea necesario ponerse en contacto con el sistema original para poder trabajar más adelante. Por ejemplo, un sistema de gestión de clientes puede lanzar eventos cuando un cliente cambie cualquiera de sus datos (como, por ejemplo, la dirección) con eventos que contengan detalles acerca de los datos que han cambiado. Un receptor puede actualizar su propia copia de datos de clientes con estos cambios, de tal modo que no necesite comunicarse con el sistema de gestión de clientes principal para poder trabajar en el futuro.

Un aspecto negativo evidente de este patrón es que se generan un montón de datos viajando de acá para allá así como montones de copias, aunque quizá esto sea un problema menor en la era del almacemaniento infinito. Lo que ganamos es una mayor resiliencia, ya que los sistemas receptores pueden funcionar en caso de que el sistema principal se vuelva inalcanzable. Se reduce la latencia, ya que no se necesitan llamadas remotas para acceder a la información. No hay que preocuparse de la carga de un sistema principal para poder hacer consultas desde otros sistemas, pero generamos más complejidad en los receptores, ya que tienen que mantener el estado, cuando lo más sencillo suele ser enviar un mensaje a la fuente cuando se necesita más información.

Generación de Eventos Event-Sourcing

La idea principal de la generación de eventos consiste en que, una vez se produce un cambio de estado de un sistema, se almacena ese cambio de estado como un evento, de tal forma que se pueda reconstruir el estado del sistema procesando todos los eventos en cualquier momento del futuro. El almacén de eventos se convierte en la Fuente Principal de Verdad y el estado del sistema es una pura derivación de él. El mejor ejemplo para los programadores es el sistema de control de versiones. El registro de todos los commits es el almacén de eventos y tu copia de trabajo es un estado del sistema.

La generación de eventos introduce muchos temas que tratar, en los cuales no voy a entrar, aunque me gustaría remarcar algunas ideas comunmente equivocadas. No hay necesidad de que el procesamiento de eventos sea asíncrono: ten en cuenta el caso de una actualización de un repositorio local de git, que es una operación totalmente síncrona, como lo es actualizar un sistema de control de versiones centralizado como subversion. Realmente tener todos esos commits te permite tener todo tipo de comportamientos interesantes, git es el mejor ejemplo, pero hacer un commit es básicamente realizar una acción única.

Otro error común es asumir que cualquiera que utilice un sistema basado en eventos debe entender cómo acceder al registro de eventos y qué datos son los interesantes. Realmente el conocimiento del registro de eventos debería estar muy limitado. Estoy escribiendo esto desde un editor de textos que no sabe nada acerca de los commits de la copia del repositorio sobre la que estoy trabajando, únicamente asume que hay un fichero en el disco. La mayor parte del procesamiento que se realiza en un sistema con generación de eventos debería basarse en una copia útil. Sólo uno de los componentes debería saber cómo generar la copia de trabajo del almacén de eventos. Podemos tener muchas copias de trabajo con diferentes esquemas si eso sirve de ayuda pero, por lo general, debería haber una separación clara entre el dominio de procesamiento y la generación de copias con las que trabajar desde el registro de eventos. Cuando se trabaja con un registro de eventos suele ser útil generar a menudo imágenes de la copia de trabajo para que no haya necesidad de procesar todos los eventos desde el principio cada vez que se necesita una copia de trabajo.

La generación de eventos tiene muchos beneficios interesantes, los cuales vienen enseguida a la mente cuando pensamos en sistemas de control de versiones. El registro de eventos provee de una fuerte capacidad de auditoría (las transacciones contables son una fuente de eventos para los balances contables). Podemos recrear estados históricos ejecutando el registro hasta cierto punto. Es posible explorar alternativas introduciendo eventos hipotéticos a medida que se ejecuta la recreación. La generación de eventos hace posible el no necesitar copias durables sobre las que trabajar, como las imágenes en memoria.

No obstante, la generación de eventos tiene sus propios problemas. Recrear eventos se vuelve un problema cuando los resultados dependen de interacciones con sistemas externos, ya es necesario imaginar cómo lidiar con esos cambios en el esquema una y otra vez. Mucha gente piensa que el procesamiento de eventos añade mucha complejidad a una aplicación, aunque supongo que es debido más a una pobre separación entre los componentes que generan la copia de trabajo y los componentes que procesan el dominio.

CQRS

La Separación de Responsabilidades entre Ordenar y Consultar (Command Query Responsibility Segregation) nace del concepto de tener estructuras de datos separadas, unas para leer información y otras para escribirla. Realmente CQRS no está relacionado con eventos, ya que puedes utilizar CQRS sin tener ningún evento presente en el diseño de tus sistemas, pero por lo general la gente combina CQRS con los patrones que hemos visto anteriormente, de ahí su presencia en el congreso.

El motivo que justifica la presencia de CQRS es que, en dominios complejos, tener un único modelo que se encargue de las lecturas y de las escrituras se acaba convirtiendo en algo muy complicado que puede ser simplificado separando ambos modelos. Resulta especialmente atractivo cuando los accesos siguen patrones diferentes, tales como muchas lecturas y muy pocas escrituras, aunque el beneficio de utilizar CQRS tiene que estar equilibrado con la complejidad adicional que puede conllevar tener modelos separados. Percibo que mis compañeros son cautelosos a la hora de usar CQRS, ya que consideran que muchas veces no se utiliza como es debido.

Haciendo que estos Patrones Tengan Sentido

Como jardinero del software que soy, que tiende a guardar algunas muestras, creo que esto es un jardín. El problema principal nace de la confusión entre patrones: en un proyecto concreto un jefe de proyecto capaz y experimentado me dijo que la generación de eventos había sido un desastre - cualquier cambio implicaba el doble de trabajo, ya que era necesario actualizar los modelos de lectura y de escritura. Precisamente creí en esa frase que quizá había alguna confusión entre generación de eventos y CQRS - así que, ¿cómo puedo adivinar quién es el culpable?. El jefe técnico del proyecto se quejaba de que el problema principal era que había demasiada comunicación asíncrona, lo cual realmente es un potenciador de la complejidad, pero que no se encuentra relacionado ni con la generación de eventos ni con CQRS. Podemos ser conscientes de que estos patrones son buenos cuando están en el lugar correcto y malos cuando los usamos en lugares incorrectos, pero es difícil saber en qué lugar nos encontramos cuando estamos mezclando los patrones.

Me encantaría escribir algún ensayo definitivo que permita disipar toda esta confusión y que dé guías útiles sobre cómo utilizar correctamente cada patrón y cuándo debe ser utilizado. Por desgracia no tengo tiempo para hacerlo. He escrito este texto con la esperanza de que sea útil, pero son consciente de que se queda corto con respecto al alcance que realmente se necesita.