Estoy haciendo un proyecto de investigación donde estoy investigando las opciones para manejar los cambios en una arquitectura de microservicios basada en eventos.
Entonces, digamos que tenemos una aplicación donde obtuvimos cuatro servicios diferentes. Cada uno de estos servicios tiene una base de datos propia para almacenar datos locales.
En esta configuración, los cuatro servicios se comunican entre sí mediante un bus de eventos. Entonces, cuando algo sucede en un servicio, publica un evento. Todos los demás servicios que estén interesados en ese evento lo procesarán a su manera.
En ese caso, los diferentes servicios de la arquitectura deben tener "contratos" sobre el contenido de estos eventos (atributos, etc.). Por lo tanto, los servicios tienen "dependencias débilmente acopladas" a estos eventos
Mi pregunta es: ¿Cómo podemos manejar los cambios en estos eventos?
Entonces, digamos que el servicio A registra nuevos usuarios en la aplicación. Por lo tanto, envía un evento "" Registrado por el usuario ". El servicio B recoge ese evento y lo procesa. Pero algunos desarrolladores del equipo del servicio C decidieron que también necesitan un género de un usuario registrado. Por lo tanto, el evento se cambia y el género del atributo se agrega al evento "UserRegistered".
¿Cómo podemos asegurarnos de que el Servicio B aún pueda recoger el mismo evento con ese atributo adicional sin volver a implementar?
¿Y hay otras formas de abordar este problema luego de versionar estos eventos?
Respuestas:
Los eventos no son sobre lo que cambió. Se trata de cuando algo cambió.
Puedo crear un sistema de eventos completamente desacoplado de los contenidos que cambiaron. De esa manera, todo lo que aprendo de un evento es que un objeto ha sido actualizado. Si incluso me importa que el objeto se haya actualizado, le diré lo que sepa cómo hablar con ese objeto para preguntarle qué cambió.
Eso no resuelve el problema de comunicar estos cambios. Simplemente evita que se convierta en parte del sistema de eventos.
Un ejemplo de una forma de resolver el problema de las diferentes versiones de datos es hacer que el observador cree y entregue una colección al objeto observado. El objeto observado llena la colección con sus últimos datos y cuando el control regresa, usted (el observador) tiene lo que necesita. Si hay algo adicional que no le importa, porque nunca lo escuchó, simplemente ignórelo.
Muchas otras formas de desollar a ese gato, pero esa es una que he hecho funcionar exactamente en este caso.
fuente
UserRegistered
evento, si hubiera un evento que no contuviera información sobre el usuario, habría 1 mensaje publicado en el bus y luego {número de servicios interesados} solicitudes al servicio de usuario o mensajes publicados al autobús. Entonces, habría {cantidad de servicios interesados} mensajes de varios tamaños. Aunque creo que este es probablemente un diseño más limpio en papel, si el rendimiento es preocupante, se descompone en cualquier sistema no trivial, especialmente a través de una red.Los marcos como NServiceBus manejan esto mediante el uso de versiones de eventos con envío de mensajes polimórficos.
Por ejemplo, la versión 1 del Servicio A podría publicar un evento como IUserRegistered_v1. Cuando el Servicio A versión 1.1 necesita incluir un campo adicional, podría declarar la interfaz IUserRegistered_v1_1, que heredaría de IUserRegistered_v1, así como declarar algunos campos adicionales.
Cuando el Servicio A publica un evento IUserRegistered_v1_1, NServiceBus enviará el mensaje a todos los puntos finales que manejan IUserRegistered_v1 o IUserRegistered_v1_1.
fuente
Mejora incremental
Un cambio simple en el modelo es que cuando los oyentes se registran como observadores, incluyen una lista u otra estructura de los elementos de datos que desean conocer. Esto puede funcionar si los datos devueltos por el servicio son simples, pero si tiene una buena cantidad de datos jerárquicos, esto puede ser realmente complicado de implementar.
Roca sólida
Si realmente desea una forma sólida de hacer este diseño, el servicio mantiene un historial de cambios que se han realizado en los datos que almacena. Esencialmente, nunca actualiza los registros en su base de datos, agrega nuevos registros donde cada uno representa el cambio. Cada uno de estos nuevos registros está asociado a una identificación de evento que identifica la acción. Se almacena un registro par con toda la información relevante sobre el cambio (quién, qué, cuándo, etc.) Esto tiene algunos otros beneficios que están fuera del alcance de esta respuesta pero que se discuten en este artículo sobre el teorema de CAP .
Cuando se realiza un cambio, crea el registro de eventos y agrega todos los datos nuevos a su base de datos. Luego publica un evento para los oyentes que contiene (mínimamente) la identificación del evento. Los oyentes pueden solicitar los datos asociados con esa identificación y obtener la versión de los datos asociados con ella. Cada oyente puede obtener lo que necesita sin acoplar las necesidades de otros oyentes diferentes. Le aconsejaría que agregue un subconjunto de los campos de datos más utilizados al mensaje de evento para que los oyentes puedan filtrar los eventos que no les interesan. Esto puede reducir la charla del proceso y algunos oyentes nunca necesitarán llamar de vuelta en absoluto. Esto también lo protege de los problemas de tiempo. Si solo vuelve a llamar al servicio y obtiene los datos según la clave, puede haber otros cambios que ocurrieron entre obtener el evento y recuperar los datos para él. Es posible que esto no sea importante para todos los oyentes, pero puede crear grandes problemas si necesita conocer todos los cambios. La mejora de diseño incremental anterior es compatible con este enfoque si realmente desea convertirlo en 11.
Algo de esto puede ser excesivo para lo que necesita hacer, pero en mi experiencia si no tiene una forma precisa de ver cómo cambia un registro con el tiempo, usted o alguien que trabaje con sus datos eventualmente lo querrá.
fuente
@CandiedOrange hace un punto válido en un comentario a su propia respuesta con respecto a los formatos de datos extensibles como xml.
No debería importar mientras agregue datos. Sin embargo, proporcione valores predeterminados razonables para eventos antiguos / campos no obligatorios.
Solo debe actualizar los servicios que le interesan, en este caso, el género. Un analizador xml / json debería poder ignorar datos adicionales para los otros servicios. Esto depende de su elección de analizador y formato de datos de eventos, por supuesto.
Sin embargo, no estoy de acuerdo con los eventos que no tienen los datos relevantes. Para el abastecimiento de eventos, los eventos deben definir lo que ha cambiado. Al recibir un evento, otros servicios no deberían tener que recuperar datos de la fuente del evento.
fuente