¿Cómo trato con los efectos secundarios en Event Sourcing?

7

Supongamos que queremos implementar un pequeño subsistema de seguridad para una aplicación financiera que advierta a los usuarios por correo electrónico si se detecta un patrón extraño. Para este ejemplo, el patrón consistirá en tres transacciones como las que se muestran. El subsistema de seguridad puede leer eventos del sistema principal desde una cola.

Lo que me gustaría obtener es una alerta que es una consecuencia directa de los eventos que ocurren en el sistema, sin una representación intermedia que modele el estado actual del patrón.

  1. Monitoreo activado
  2. Transacción procesada
  3. Transacción procesada
  4. Transacción procesada
  5. Alerta activada (id: 123)
  6. Correo electrónico para la alerta enviada (para id: 123)
  7. Transacción procesada

Teniendo esto en cuenta, pensé que la fuente de eventos podría aplicarse aquí muy bien, aunque tengo una pregunta sin una respuesta clara. La alerta activada en el ejemplo tiene un claro efecto secundario, se debe enviar un correo electrónico, una circunstancia que solo debería ocurrir una vez. Por lo tanto, no debería ocurrir cuando se reproducen todos los eventos de un agregado.

Hasta cierto punto, veo el correo electrónico que debe enviarse de manera similar a las materializaciones generadas por el lado de la consulta que he visto tantas veces en la documentación de CQRS / Evento de abastecimiento, aunque con una diferencia no tan sutil. p>

En esta literatura, el lado de la consulta se construye a partir de controladores de eventos que pueden generar una materialización del estado en un punto dado, leyendo de nuevo todos los eventos. En este caso, sin embargo, eso no se puede lograr exactamente de la misma manera por las razones explicadas anteriormente. La idea de que cada estado es transitorio no se aplica tan bien aquí . Necesitamos registrar el hecho de que se envió una alerta a algún lugar.

Una solución fácil para mí sería tener una tabla o estructura diferente donde mantendré registros de las alertas que se activaron anteriormente. Como tenemos una identificación, podríamos verificar si se emitió una alerta con la misma identificación anteriormente. Tener esta información haría que el identificador de comando SendAlertCommand. Se pueden emitir varios comandos, pero el efecto secundario solo ocurrirá una vez.

Incluso teniendo esa solución en mente, no sé si esto es un indicio de que hay algo mal con esta arquitectura para este problema.

  • ¿Mi enfoque es correcto?
  • ¿Hay algún lugar donde pueda encontrar más información sobre esto?

Es extraño que no haya podido encontrar más información sobre esto. Tal vez he estado usando la redacción incorrecta.

¡Muchas gracias!

    
pregunta IoChaos 24.07.2017 - 17:12

2 respuestas

11
  

¿Cómo me ocupo de los efectos secundarios en la obtención de eventos?

Versión corta: el modelo de dominio no realiza efectos secundarios. Se sigue a ellos. Los efectos secundarios se realizan utilizando un puerto que se conecta al límite; cuando se envía el correo electrónico, envía el acuse de recibo al modelo de dominio.

Esto significa que el correo electrónico se envía fuera de la transacción que actualiza la flujo de eventos.

Precisamente donde, afuera, es una cuestión de gusto.

Entonces, conceptualmente, tienes un flujo de eventos como

EmailPrepared(id:123)
EmailPrepared(id:456)
EmailPrepared(id:789)
EmailDelivered(id:456)
EmailDelivered(id:789)

Y desde este flujo puedes crear un pliegue

{
    deliveredMail : [ 456, 789 ],
    undeliveredMail : [123]
}

El pliegue le dice qué correos electrónicos no han sido reconocidos, por lo que los envía nuevamente:

undeliveredMail.each ( mail -> {
    send(mail);
    dispatch( new EmailDelivered.from(mail) );
}     

Efectivamente, este es un compromiso de dos fases: estás modificando SMTP en el mundo real, y luego estás actualizando el modelo.

El patrón anterior le ofrece un modelo de entrega al menos una vez. Si lo desea como máximo una vez, puede darle la vuelta

undeliveredMail.each ( mail -> {
    commit( new EmailDelivered.from(mail) );
    send(mail);
}     

Hay una barrera de transacción entre hacer que el correo electrónico sea duradero y enviar el correo electrónico. También hay una barrera de transacción entre enviar el correo electrónico y hacer que el correo electrónico sea duradero.

mensajería confiable con transacciones distribuidas de Udi Dahan puede ser un buen punto de partida.

    
respondido por el VoiceOfUnreason 24.07.2017 - 18:44
2

Debe separar 'Eventos de cambio de estado' de 'Acciones'

Un Evento de cambio de estado es un evento que cambia el estado del objeto. Estos son los que almacena y reproduce.

Una Acción es algo que el objeto hace con otras cosas. Estos no se almacenan como parte de Event Sourcing.

Una forma de hacerlo es con los manejadores de eventos, que conectas o no, dependiendo de si deseas ejecutar las Acciones.

public class Monitor
{
    public EventHander SoundAlarm;
    public void MonitorEvent(Event e)
    {
        this.eventcount ++;
        if(this.eventcount > 10)
        {
             this.state = "ALARM!";
             if(SoundAlarm != null) { SoundAlarm();}
        }
    }
}

Ahora en mi servicio de monitoreo puedo tener

public void MonitorServer()
{
    var m = new Monitor(events); //11 events
    //alarm has not been sounded because the event handler wasn't wired up
    //but the internal state is correctly set to "ALARM!"
    m.SoundAlarm += this.SendAlarmEmail;
    m.MonitorEvent(e); //email is sent
}

Si necesita registrar los correos electrónicos enviados, puede hacerlo como parte de SendAlarmEmail. Pero no son eventos en el sentido de aprovisionamiento de eventos

    
respondido por el Ewan 24.07.2017 - 18:31

Lea otras preguntas en las etiquetas