jueves, 5 de julio de 2018

Los eventos nunca fallan

Seguro que cuando has leído el título has pensado: "!Y un huevo que no!". Déjame explicarme un poco más para que se entienda el título de esta entrada.

Hace poco he charlado sobre arquitectura con otro desarrollador de software y me hizo un par de preguntas que no tienen sentido sobre que pasa cuando falla un evento que me dejó bastante mosqueado con respecto a si ese desarrollador tiene completamente claras las cosas o tiene algún concepto que le baila un poco y no está del todo encajado en el puzle.


La primera fue:

"Tienes un gestor de procesos de larga duración (AKA Saga) que coordina un proceso entre dos servicios. ¿Que se hace cuando algún evento falla?"

Mi respuesta fue que no se debe usar una Saga para coordinar entre 2 servicios. Cada servicio debe tener su Saga y los servicios notifican los eventos de dominio que les atañen a través de la capa de aplicación que usa la Saga para aplicar el evento que recibe.

Si asumimos que cada servicio es un Bounded Context; una Saga que coordinase acciones de ambos servicios estaría saltándose las fronteras entre contextos y esto es malo y nunca se debe hacer.

Una Saga debe responder única y exclusivamente de los eventos en los que esté interesado el contexto al que pertenece. Y las acciones que debe tomar la Saga al recibir los eventos deben estar únicamente relacionado con ese contexto. La Saga utilizaría los agregados y servicios de dominio de ese contexto para realizar la acción pertinente y sería el evento de dominio generado de tal acción el que fuese al bus de mensajes y llegase al otro servicio. En caso de querer desacoplar la Saga del código del servicio (altamente recomendable) se puede utilizar un patrón mediador o el propio bus de mensajes para que se notifique que la saga ha cambiado de estado debido a que ha pasado algo en otro sitio; pero ese mensaje es relativo y exclusivo a su bounded context. Una Saga nunca debe lanzar un evento que sea consumido por otro bounded context.  puede actuar recibiendo eventos de otro contexto.  El evento que es consumido por otro bounded context es el evento que se genera cuando el contexto de origen ha recibido el mensaje de la saga y ha realizado las acciones pertinentes a través del agregado de turno que se a encargado de comprobar las reglas del dominio de este contexto. Esto asegura que el contexto de origen está en un estado consistente y válido antes de notificar a los otros contextos que algo ha cambiado.

Si, en respuesta a este evento, el otro servicio necesita mantener también su máquina de estados a la espera de otro evento; entonces este otro servicio debe tener su propia Saga con su propio estado relacionado, sus propios agregados y sus propios servicios de dominio y quedar en espera del evento de dominio generado por los agregados (no la Saga) del contexto de origen.

Como esto no respondía del todo a la pregunta sobre que hacer cuando falla un evento; en otra ocasión me pregunto lo mismo directamente; quitando de por medio el concepto erróneo de Saga coordinadora anterior:

"¿Qué se hace para corregir la consistencia cuando la aplicación de un evento falla en el servicio destino?"

Como asumo que corregir la consistencia se refiere al servicio origen del evento; mi respuesta fue "Nada. Los eventos nunca fallan". Me explico:

La mayoría de los buses de mensajes proveen algún tipo de persistencia y otras medidas (retry, error queues, etc) para poder hacer un replay del evento en caso de no poder controlarse correctamente en ese momento. Si el evento falla por alguna caída en la infraestructura como la red o el motor de persistencia simplemente ya le llegará de una manera u otra (aunque sea manual) y lo podrá aplicar cuando arreglemos la caída.

Con respecto a la consistencia de información y reglas de dominio la cosa está clara: Los eventos no fallan nunca.

Un evento es un hecho pasado, ya ha ocurrido, no puede, ni debe, cancelarse o deshacerse. Un evento de dominio se genera y se notifica cuando hemos aplicado las invariantes y reglas de dominio de un agregado en un contexto y se han realizado las modificaciones pertinentes que dejan los datos de mi contexto en un estado correcto y consistente

Si al notificar un evento recibimos instrucciones para deshacer la acción debido a que el sistema que recibe el evento (otro bounded context diferente) no puede tomas las medidas necesarias para aplicarlo también debido a invariantes y reglas de dominio entonces lo que ocurre es que tenemos mal diseñada la segregación de los contextos y por tanto mal diseñados los agregados. Estamos intentando coordinar agregados y esto es un error; un agregado debe ser una unidad transaccional completa y válida.

Si un agregado dice que una acción se ha realizado correctamente; no debería haber nada en el mundo que le lleve la contraria. Ergo: Los eventos nunca fallan porque solo se generan cuando ya está realizado el cambio y este cambio es válido si o si. Lo dice el único que tiene que ser el encargado de confirmarlo, el agregado.

No hay comentarios:

Publicar un comentario