Después de investigar un poco más, me topé con este artículo del que saqué algunas citas que creo que son útiles para lo que quiero lograr (y para cualquier lector futuro). Esto ofrece una manera de adoptar un modelo de programación reactiva sobre un modelo de programación imperativo.
Abastecimiento de eventos
La idea aquí es representar la transición de estado de cada aplicación en forma de un evento inmutable. Los eventos se almacenan en forma de registro o diario a medida que ocurren (también conocido como 'almacén de eventos'). También pueden consultarse y almacenarse indefinidamente, con el objetivo de representar cómo el estado de la aplicación, en su conjunto, evolucionó con el tiempo.
Lo que esto ayuda a lograr es que si un microservicio deja de funcionar y se publican otros eventos pertinentes y , por ejemplo, otros eventos de ese microservicio los consumen, cuando ese microservicio vuelve a funcionar, puede referirse a esto event storepara recuperar todos los eventos que se perdió durante el período en que cayó.
Apache Kafka como corredor de eventos
Considere el uso de Apache Kafka, que puede almacenar y enviar miles de eventos por segundo y tiene mecanismos incorporados de replicación y tolerancia a fallas. Tiene un almacén persistente de eventos que pueden almacenarse en el disco de forma indefinida y consumirse en cualquier momento (pero no eliminarse) del Tema (cola de fantasía de Kafka) en el que se entregaron.
A los eventos se les asignan compensaciones que los identifican de manera unívoca dentro del Tema: Kafka puede administrar las compensaciones en sí, proporcionando fácilmente semántica de entrega "como máximo una vez" o "al menos una vez", pero también se pueden negociar cuando un consumidor de eventos se une a un Tema , lo que permite que los microservicios comiencen a consumir eventos desde cualquier lugar arbitrario en el tiempo, generalmente desde donde el consumidor lo dejó. Si el último desplazamiento de evento consumido persiste transaccionalmente en el almacenamiento local de los servicios cuando los casos de uso se "completan con éxito", ese desplazamiento se puede usar fácilmente para lograr una semántica de entrega de evento "exactamente una vez".
De hecho, cuando los consumidores se identifican con Kafka, Kafka registrará qué mensajes fueron entregados a qué consumidor para que no se vuelva a enviar.
Sagas
Para casos de uso más complejos donde la comunicación entre los diferentes servicios es realmente necesaria, la responsabilidad de terminar el caso de uso debe estar bien reconocida: el caso de uso está descentralizado y solo finaliza cuando todos los servicios involucrados reconocen que su tarea se completó con éxito, de lo contrario, todo el caso de uso debe fallar y se deben activar medidas correctivas para deshacer cualquier estado local no válido.
Esto es cuando entra en juego la saga. Una saga es una secuencia de transacciones locales. Cada transacción local actualiza la base de datos y publica un mensaje o evento para activar la próxima transacción local en la saga. Si una transacción local falla porque viola una regla comercial, la saga ejecuta una serie de transacciones compensatorias que deshacen los cambios realizados por las transacciones locales anteriores. Lea esto para más información.