Estoy diseñando un sistema que utiliza Event Sourcing, CQRS y microservicios. Tengo que entender que este no es un patrón poco común. Una característica clave del servicio debe ser la capacidad de rehidratarse / restaurarse de un sistema de registro. Los microservicios producirán comandos y consultas en un MQ (Kafka). Otros microservicios responderán (eventos). Los comandos y consultas se mantendrán en S3 con el propósito de auditar y restaurar.
El proceso de pensamiento actual era que, con el fin de restaurar el sistema, podríamos extraer el registro de eventos de S3 y simplemente enviarlo a Kafka.
Sin embargo, esto no reconoce cambios en productores y consumidores a lo largo del tiempo. El control de versiones en el nivel de comando / consulta parece ir de alguna manera hacia la solución del problema, pero no puedo entender cómo versionar a los consumidores de modo que pueda aplicar que cuando un comando, durante una restauración, se recibe y procesa, es exactamente el mismo [versión del] código que realiza el procesamiento, ya que fue la primera vez que se recibió el comando.
¿Hay algún patrón que pueda usar para resolver esto? ¿Alguien sabe de otros sistemas que anuncian esta característica?
EDITAR: Agregar un ejemplo.
Un 'comprador' envía una 'pregunta' a un 'vendedor' en mi sitio de subastas. El flujo tiene el siguiente aspecto:
UI -> Web App: POST /question {:text text :to seller-id :from user-id}
Web App -> MQ: SEND {:command send-question :args [text seller-id user-id]}
MQ -< Audit: <command + args appended to log in S3>
MQ -< Questions service: - Record question in DB
- Email seller 'You have a question'
Ahora, como resultado de un nuevo requisito comercial, ajusto el consumidor del 'Servicio de preguntas' para que continúe contando todas las preguntas no leídas. El esquema de base de datos ha cambiado. Hasta ahora no teníamos ni idea de si el vendedor leía o no una pregunta. La última línea se convierte en:
MQ -< Questions service: - Record question in DB
- Email seller 'You have a question'
- Increment 'unread questions count'
Dos comandos son problemas, uno antes del cambio, uno después del cambio. El 'conteo de preguntas no leídas' es igual a 1.
El sistema se bloquea. Restauramos al reproducir los comandos a través del nuevo código. Al final de la restauración, nuestro 'conteo de preguntas no leídas' es igual a 2. Aunque, en este ejemplo artificial, el resultado no es una catástrofe, el estado que ha sido restaurado no es lo que era antes.
fuente
Respuestas:
Primero, es importante comprender y poder aprovechar la diferencia entre Comandos y Eventos.
Como esta pregunta señala sucintamente, los comandos son cosas que nos gustaría que sucedan, y los eventos son cosas que ya han sucedido. Un comando no necesariamente resulta en un evento significativo en el sistema, pero generalmente lo hace. Por ejemplo, un
send message
comando puede ser rechazado, en cuyo caso no ocurre ningún evento (por lo general, un error no se consideraría un evento en este sentido, aunque aún podemos elegir registrarlo en un registro de diagnóstico). Ahora, sisend message
se acepta el comando,message sent
se produce el evento y los detalles del evento podrían describir al remitente, al receptor y al contenido.Cuando hablamos sobre el estado del sistema, en realidad estamos discutiendo no una culminación de comandos, sino de eventos. Solo los eventos pueden causar un cambio de estado en el sistema. Para sacar un ejemplo de la vida, supongamos que voy al supermercado local Publix y compro un boleto de lotería de Florida. El comando era "Comprar boleto" y el evento era "Boleto emitido". Mi siguiente comando entonces es a la lotería para dibujar mis números para el PowerBall. La lotería va a ignorar mi comando (pero no tengo conocimiento), y el evento "Números de PowerBall elegidos" tiene lugar independientemente de mis deseos. Si mis números coinciden, me ocurre el evento "Jackpot ganó" (y creo que se escuchó mi comando). Si no, me doy cuenta de que mi comando fue ignorado.
Desde una perspectiva histórica, la lotería solo está interesada en un subconjunto de eventos. A la lotería solo le importa que (a) se haya emitido un boleto, (b) se hayan elegido los números y (c) se haya ganado el premio mayor. Esos son los artículos de interés. El acto de comprar el boleto, querer ganar, etc., son irrelevantes, como es lo que hago con mi boleto después de perder. Si bien el mundo real cambia para los eventos mundanos, solo necesitamos registrar aquellos eventos que son importantes para nuestro sistema.
En teoría, bajo una técnica de obtención de eventos, se puede reproducir una secuencia de eventos desde el principio de los tiempos para llegar al estado actual. Esto se basa en el supuesto de que las condiciones subyacentes del sistema son constantes y deterministas. Sin embargo, estos supuestos no son válidos en muchos sistemas. Los datos asociados con un evento, así como los tipos de eventos que nos interesan, pueden cambiar a medida que evoluciona nuestro software. Además, puede ser computacionalmente costoso volver a calcular el estado actual en respuesta a cada consulta. Por esta razón, las instantáneas del estado del sistema a menudo se toman para representar puntos conocidos en el tiempo, a los cuales se pueden agregar los eventos más recientes.
Si bien aún es posible reproducir una secuencia de eventos en varias versiones, es probable que la cantidad de esfuerzo humano involucrado en hacerlo sea prohibitiva. A menos que haya una razón justificable para diseñar esa capacidad en el sistema, es mejor que construya su sistema para utilizar instantáneas.
Ejemplo en cuestión
En el ejemplo dado en la pregunta, la arquitectura no está realmente basada en eventos; Está basado en comandos. La reproducción de comandos crea el estado del sistema. Este es un antipatrón y debe repararse. En cambio, los eventos principales son:
Cada uno de estos eventos puede "reproducirse" para dar el estado actual. Por ejemplo, en el acto de hacer una pregunta, el comportamiento del sistema podría ser enviar un correo electrónico al vendedor e incrementar el
unanswered question
contador. Este comportamiento puede ser cambiado; sin embargo, el hecho de que se hizo la pregunta no lo hace. Del mismo modo, el sistema puede disminuir elunanswered question
contador cuando el vendedor responde. Este comportamiento es modificable, pero el hecho de que el vendedor respondió no lo es.La mayoría de los sistemas de abastecimiento de eventos calcularían dinámicamente el recuento de preguntas sin respuesta al reproducir el flujo de eventos específico en respuesta a una consulta.
fuente
Para auditar, claro. ¿Para restaurar ? Eso es raro y es probable que te cause dolores de cabeza.
Si va a ser fuente de eventos, desea rehidratar el estado de los eventos (cosas que sucedieron en el pasado) no los comandos. Esto le ahorra la mayoría de los problemas asociados con los cambios en la implementación de comandos: solo necesita lidiar con los cambios de estado persistentes.
El control de versiones sigue siendo una preocupación. En particular, debe asegurarse de que sus eventos persistentes sean lo más flexibles posible (representaciones de DTO, en lugar de serializaciones directas de los conceptos en su dominio). Al leer eventos de la tienda, tiene la oportunidad de actualizarlos según sea necesario antes de aplicarlos al estado rehidratante.
fuente