2012-06-13 30 views
12

Ejemplo: Las reglas comerciales establecen que el cliente debe recibir un mensaje de confirmación (correo electrónico o similar) cuando se realiza un pedido.CQRS: ¿cuándo enviar un mensaje de confirmación?

Digamos que un NewOrderRegisteredEvent se envía desde el dominio y es recogido por un oyente de eventos que envía el mensaje de confirmación. Cuando se hace eso, otro manejador de eventos lanza una excepción o algo más sale mal y la unidad de trabajo se revierte. Ahora hemos enviado al usuario un mensaje de confirmación de algo que se ha retrotraído.

¿Cuál es la forma "cqrs" de resolver problemas como este en los que desea hacer algo una vez que se ha comprometido una unidad de trabajo? Otro factor que complica es la repetición de eventos. No quiero que los mensajes de confirmación antiguos se vuelvan a enviar cada vez que reproduzco los eventos grabados para construir una nueva vista/proyección.

Mi mejor teoría hasta el momento: acabo de comenzar a investigar el fascinante mundo de las cqrs y me preguntaba si esto es algo que se implementaría como una saga. Si una saga es como una máquina de estados donde cada transición solo puede tener lugar una sola vez, entonces supongo que eso resolvería este problema. Sólo tengo un tiempo difícil visualizar cómo va a encajar con los eventos en el bus de mando y dominio ..

+1

Haga que la tarea "enviar correo electrónico" sea un mensaje. Si el flujo retrocede, también lo hace el envío del mensaje (al almacenamiento duradero). Algo más capta el mensaje y lo hace por correo electrónico. Por cierto, no relacionados con cqrs. Llanura distribuida informática sentido común. –

+0

En cuanto a la repetición de eventos, NUNCA induce este tipo de comportamiento. Si tu impl. lo hace, lo estás haciendo mal. –

+0

Eso tiene sentido, pero estoy interesado en esto en un contexto cqrs. ¿Qué es un "mensaje"? ¿Y a quién/qué lo captaría y cuándo? Sería bueno aprovecharse de la tienda de eventos para evitar la introducción de otro mecanismo de almacenamiento para realizar un seguimiento de la comunicación saliente. – Kimble

Respuesta

16
  1. Un evento solo debe ocurrir después de que la transacción se haya completado. Si algo sale mal y hay un retroceso, entonces el evento no ocurrió desde un punto de vista externo. Por lo tanto, no debería publicarse en absoluto. Aunque un evento OrderRegistrationFailed podría publicarse si es necesario.

  2. No querrá que se envíe el correo a menos que el comando se haya ejecutado correctamente.

    Primero algunas razones por las cuales el controlador de comando - como se propone en otra respuesta - sería el lugar equivocado: en algunas circunstancias, el controlador de comando no podría decir si el comando finalmente tendrá éxito o no. Hacer que el manejador de comandos invoque el envío del correo también pondría el conocimiento del proceso dentro del controlador de comando, lo que rompería el SRM y uniría las reglas comerciales con la capa de aplicación.

    El correo debe enviarse después del hecho, es decir, desde un controlador de eventos.

    Para evitar que este controlador se dispare durante la reproducción, simplemente no puede registrarlo. Esto funciona de manera similar a cómo prueba su aplicación. Solo registra los manejadores que realmente necesita.

    sistema
    • Producción -> Registrar todos los controladores de eventos
    • Pruebas -> registrar solamente los controladores de eventos probados
    • Replay -> Registrar sólo los controladores de proyección/desnormalización

Otra - aún más débilmente acoplado, aunque un poco más complejo - la posibilidad sería tener un Saga manejar el NewOrderRegisteredEvent y emitir un comando SendMail al appr contexto limitado de merecido (gracias, Yves Reynhout, por señalar esto en los comentarios de la pregunta).

+1

¡Esta es una de las mejores respuestas que he recibido aquí en stackoverflow! Entonces, los eventos de dominio se almacenarán en algún lugar y solo se publicarán fuera del dominio (para proyecciones/desnormalizadores) una vez que los eventos se hayan escrito correctamente en el registro de eventos. Supongo que escribir en la tienda de eventos ocurre dentro de una transacción (generalmente) inicializada por un manejador de comandos/interceptor? – Kimble

+0

Básicamente sí, el almacenamiento de uno o más eventos (ya sea en un registro de eventos o en un almacén de eventos, tenga cuidado de no confundirlos) sería la última parte de la transacción. Solo después de un almacenamiento exitoso serán despachados y procesados, posiblemente incluso de forma asíncrona. –

+0

¿Cuál es la diferencia entre un registro de eventos y una tienda? Estoy bastante seguro de que los he visto intercambiables cuando leo sobre cqrs. – Kimble

2

Hay dos soluciones posibles

1) La publicación del evento y el control del evento (es decir, el correo electrónico) son parte de una sola transacción. En este caso, su marco de transacciones se ocupa de ello por usted. Si el correo electrónico falla, se revierte el evento. Es probable que vuelva a intentar el comando. Esto es conceptualmente limpio y fácil de pensar. Ningún evento ha terminado de publicarse hasta que todos los que tienen algo que decir al respecto hayan expresado su opinión. Sin embargo, en términos prácticos, esto puede ser doloroso, ya que normalmente implica transacciones distribuidas. Estos son difíciles de conseguir. ¿Puede su cliente de correo electrónico inscribirse en la misma transacción que la base de datos que está llevando a cabo sus eventos?

2) La publicación del evento es transaccional, pero los manejadores de eventos tratan las transacciones a su manera. El controlador de eventos que envía correos electrónicos podría hacer un seguimiento de los eventos que había visto. Si se bloqueaba, solicitaría eventos antiguos y los procesaría. Podría tomar una decisión comercial sobre qué tan grande sería si las personas tuvieran correos electrónicos perdidos o duplicados. (Para las transacciones relacionadas con el dinero, probablemente la respuesta sea que no debe permitirlo).

La solución (2) es típicamente lo que ve promovido en los círculos DDD/CQRS ya que es la solución menos flexible. La solución (1) es bastante práctica en un sistema pequeño donde la tienda de eventos y las proyecciones están en una única base de datos y las proyecciones no cambian con frecuencia. La solución (2) permite que una diversidad de manejadores de eventos trabajen a su manera. La solución (1) puede ocasionar que muchas preocupaciones que no se superponen se encripten. En este caso, las reglas comerciales de su orden no se completan hasta que se resuelven las muchas cosas extrañas que ocurren en el envío de correos electrónicos. Por un lado, puede ralentizarte un poco.

Si el envío del correo electrónico fue más interesante que "vio el evento, envió el correo electrónico", entonces tiene razón, es posible que tenga una saga o un flujo de trabajo en sus manos. El correo electrónico en operaciones grandes es a menudo un sistema complejo por derecho propio, del cual es improbable que tenga que implementar gran parte. Solo necesita asegurarse de poner su correo electrónico en una cola de solicitudes de algún tipo (utilizando el método (2)), y es probable que el sistema de correo haga reintentos/lotes/evitación de correo no deseado/trabajo nocturno/etc.

+0

¡Gracias por la respuesta larga! Las transacciones distribuidas son un dolor. En cuanto a la solución número dos, ¿implicaría introducir un nuevo mecanismo de almacenamiento para realizar un seguimiento de los correos electrónicos que ha enviado, o también es común aprovechar la tienda de eventos para esto? Veo algunos problemas potenciales con los controladores de eventos que publican sus propios eventos. Especialmente durante una reproducción donde el evento "correo electrónico enviado x" vendría después del evento, el correo electrónico se activó en primer lugar. – Kimble

+0

Si desea un envío de correo electrónico realmente transaccional, tendrá que manejarlo de alguna manera. Tal vez guardando la identidad de un correo electrónico que haya enviado exitosamente al disco.¿Pero la mayoría de las operaciones de correo electrónico de grado comercial no garantizan la entrega si escribe el mensaje que necesita en alguna cola? –

+0

El envío de correo electrónico verdaderamente transaccional está fuera del alcance en este momento. Tenía curiosidad acerca de cómo se trataba este problema en cqrs, especialmente en conjunción con la repetición del evento. Pero, sí, colocar el correo electrónico en una cola y almacenar una identificación de correlación en la tienda del evento sería de gran ayuda para garantizar la entrega. – Kimble

Cuestiones relacionadas