2012-09-09 40 views
12

Hay una cosa sobre CQRS que no entiendo: Cómo actualizar el modelo de lectura cuando el evento planteado no contiene los detalles necesarios para actualizar el modelo de lectura.Los eventos CQRS no contienen los detalles necesarios para actualizar el modelo de lectura

Lamentablemente, este es un escenario bastante común.

Ejemplo: agrego un usuario a un grupo, entonces envío un comando addUserToGroup (userId, groupId). Esto se recibe, manejado por el manejador de comandos, el evento userAddedToGroup se crea, almacena y publica.

Ahora, un controlador de eventos recibe este evento y las dos ID. Ahora habrá una vista que enumere todos los usuarios con los nombres de los grupos en los que se encuentran. Para actualizar el modelo de lectura para esa vista, necesitamos la identificación del usuario (que tenemos) y el nombre del grupo (que no usamos). t have, solo tenemos su id).

Entonces la pregunta es: ¿Cómo manejo este escenario?

En la actualidad, cuatro opciones vienen a la mente, todos con sus desventajas específicas:

  1. El modelo de lectura pide el dominio. => Prohibido, y ni siquiera es posible, ya que el dominio solo tiene comportamiento, no estado (público).

  2. El modelo de lectura lee el nombre del grupo de otra tabla en el modelo de lectura. => Funciona, pero ¿qué pasa si no hay una tabla que coincida?

  3. Agregue los datos necesarios para el evento. => No funciona, ya que esto significa que también tuve que actualizar todos los eventos anteriores, y no puedo prever qué datos necesitaré algún día.

  4. No maneje el evento mediante un controlador de eventos "habitual", pero inicie un proceso ETL en segundo plano que trate con el almacén de eventos, cree los datos necesarios y escriba el modelo de lectura. => Funciona, pero para mí esto parece un poco excesivo para un escenario tan simple.

Entonces, la pregunta es: ¿Cómo manejo este escenario correctamente?

Respuesta

5

Los eventos no representan necesariamente una asignación uno a uno de los comandos que han iniciado el proceso en primer lugar.Por ejemplo, si usted tiene una orden:

SubmitPurchaseOrder 
    Shopping Cart Id 
    Shipping Address 
    Billing Address 

El evento resultante podría ser similar al siguiente:

PurchaseOrderSubmitted 
    Items (Id, Name, Amount, Price) 
    Shipping Address 
    Shipping Provider 
    Our Shipping Cost 
    Shipping Cost billed to Customer 
    Billing Address 
    VAT % 
    VAT Amount 
    First Time Customer 
    ... 

Por lo general, la información está disponible para el modelo de dominio (ya sea por estar proporcionada por el comando o como estado interno conocido del agregado en cuestión o se calcula como parte del procesamiento.)

Además, el evento puede enriquecerse consultando el modelo de lectura o incluso un BC diferente (por ejemplo, recuperar el% IVA real dependiendo del estado) durante el proceso ng.

Está asumiendo correctamente que los eventos pueden (y probablemente lo harán) cambiar con el tiempo. Esto básicamente no importa en absoluto si emplea el control de versiones: agregue el nuevo evento (por ejemplo, SubmitPurchaseOrderV2) y agregue un controlador de eventos apropiado a todas las clases que se supone que lo consumen. No es necesario cambiar el evento anterior, todavía puede consumirse, ya que no modifica la interfaz, la extiende. Básicamente se trata de un muy buen ejemplo del principio abierto/cerrado en la práctica.

+0

Bien, hasta ahora, lo he obtenido ;-) Lo que no he conseguido aún es: Supuse que mi evento PurchaseOrderStortada _today_ no contiene el IVA. Ahora, avance rápido en algún punto en el futuro, donde lo haga. Por supuesto que puedo crear un nuevo evento, básicamente V2.0, que contiene el IVA, pero eso no me ayuda con todos los pedidos del pasado, ¿o sí? ¿Cómo puedo lidiar con eso? –

+1

Puede cambiar la estructura del modelo y la estructura de los eventos de forma independiente. Una vez que su modelo se amplía con una nueva información (como el IVA), los manejadores del evento anterior simplemente permanecen tal como están o establecen la nueva propiedad a un valor predeterminado. Al igual que lo haría al agregar una nueva columna en una tabla de base de datos: La nueva columna debe establecerse en nulo o en algún valor predeterminado para todas las entradas históricas. Cada vez que se repita un evento o se use para hidratar un modelo de lectura, el nuevo campo se establecerá con el valor predeterminado que necesite. –

+0

Bien, gracias, tiene sentido :-)! –

7

Hay dos soluciones comunes.

1) "Enriquecimiento de eventos" es donde de hecho colocas información sobre el evento que refleja la información que estás mencionando, p. el nombre del grupo. Hacer esto es en algún lugar entre modelar tu dominio correctamente y hacer trampa. Si sabe, por ejemplo, que los nombres de los grupos cambian, emitir el nombre en el momento del cambio no es una mala idea. Imagine que cuando crea un artículo de línea en un presupuesto o una factura, desea emitir el precio del bien vendido en el evento creado de la factura. Esto se debe a que debe respetar ese precio, incluso si cambia más tarde.

2) Proyecte varias secuencias a la vez. Escriba un proyector que mire información de varias corrientes y las una. Es posible que vea eventos de usuarios y grupos, así como su usuario agregado al evento grupal. Según el orden de los eventos en su sistema, puede saber que un usuario está en un grupo antes de saber el nombre del grupo, pero debe conocer las propiedades generales de su tienda de eventos antes de comenzar.

+0

Gracias, eso clarificó mucho :-)! –

+1

No estoy seguro de entender el # 2. ¿La proyección consulta la transmisión o simplemente espera un nuevo evento? ¿Hay algún trabajo clave que pueda usar para buscar este proceso? Aprendí "Enriquecimiento de eventos" pero parece ser un mal sln. – Raif

+0

Una proyección suele ser un componente que ejecuta "continuamente" la lectura de una o más secuencias a medida que emiten eventos y proyecta los cambios en un modelo de lectura, como una base de datos relacional. –

0

Opción 2 estaría bien, su pregunta sobre "qué pasa con el desajuste en la tabla de modelo de lectura de nombre de los grupos" no se aplicaría. no se deben eliminar datos, se debe invalidar cuando se emita un evento previo (por ejemplo, eliminar grupo). Al final, la fila en la tabla de grupos está allí efectivamente y puede leer el nombre del grupo sin ningún problema. El único problema aparente podría ser la inconsistencia de velocidad, pero ese es otro problema, los eventos deben procesarse ordenadamente sin importar la velocidad con la que se procesan.

Cuestiones relacionadas