¿Cómo encajan los repositorios con CQRS?


Según Fowler (aquí), un repositorio "media entre el dominio y las capas de asignación de datos, actuando como una colección de objetos de dominio en memoria."Así, por ejemplo, en mi aplicación de Servicio de Mensajería, cuando se envía una nueva ejecución, mi servicio de aplicación crea un nuevo objeto raíz agregado de ejecución, lo rellena con valores de la solicitud y luego lo agrega al RunRepository antes de llamar a la Unidad de Trabajo para guardar los cambios en la base de datos. Cuando un usuario quiere ver la lista de corridas actuales, hago una consulta al mismo repositorio y devuelvo un DTO desnormalizado que representa la información.

Sin embargo, al mirar CQRS, la consulta no llegaría al mismo repositorio. En cambio, tal vez iría directamente contra el almacén de datos y siempre se desnormalizaría. Y mi lado de comando evolucionaría a un NewRunCommand y Manejador que crearía y completaría un objeto de dominio NewRun y luego persistiría la información en el almacén de datos.

Así que la primera pregunta es dónde hacer ¿los repositorios encajan en el modelo CQRS si no mantenemos una colección en memoria (caché, si se quiere) de objetos de dominio?

Considere el caso en el que la información enviada al servicio de mi aplicación no contiene más que una serie de valores de ID que el servicio debe resolver para construir el objeto de dominio. Por ejemplo, la solicitud contiene el ID # del courier asignado a la ejecución. El servicio debe buscar el objeto Courier real en función del valor de ID y asignar el objete al NewRun usando el método AssignCourier (que valida el courier y realiza otra lógica de negocio).

La otra pregunta es, dada la separación para las consultas y la posible ausencia de repositorios, ¿cómo realiza el servicio de aplicación la búsqueda para encontrar el objeto de dominio Courier?

ACTUALIZACIÓN

Basado en alguna lectura adicional y pensamiento después del comentario de Dennis, voy a reformular mis preguntas.

Me parece que CQRS fomenta los repositorios que son meras fachadas sobre los mecanismos de acceso y almacenamiento de datos. Dan la "apariencia" de una colección (como Fowler describe) pero no están administrando las entidades en memoria (como Dennis señaló). Esto significa que cada operación en el repositorio es de paso, ¿sí?

¿Cómo encaja una Unidad de Trabajo en este enfoque? Normalmente se usa un UoW para confirmar los cambios realizados en un repositorio (¿correcto?) pero si el repositorio no mantiene las entidades en memoria, entonces, ¿qué papel tiene un UoW?

Con respecto a una operación de' escritura', ¿tendría el manejador de comandos una referencia al mismo repositorio, un repositorio diferente o quizás un UoW en lugar de un repositorio?

Author: SonOfPirate, 2012-04-07

2 answers

He leído acerca de los sistemas CQRS que mantienen un simple almacén de valores clave en el lado de comandos para representar el estado de una aplicación, y otros que simplemente correlacionan mensajes (usando algún tipo de saga) y utilizan el almacén de consultas para representar un estado de aplicaciones en su lugar. De cualquier manera, sin duda habrá una tecnología de persistencia involucrada con estos enfoques, pero el patrón de repositorio en estos casos sería una abstracción innecesaria sobre la parte superior de la misma.

Mi experiencia con CQRS ha sin embargo, solo hemos estado con el abastecimiento de eventos, donde hemos reproducido eventos pasados para reconstruir agregados que encapsulan e imponen la lógica e invariantes del negocio. En este caso, el patrón de repositorio es una abstracción familiar que puede proporcionar una forma más sencilla de recuperar cualquiera de estos agregados.

Con respecto al lado de la consulta, recomiendo acercarse lo más posible al almacén de datos, con esto me refiero a evitar repositorios, servicios o fachadas, etc. entre tu IU (sea lo que sea) y su almacén de datos.

Podría ayudar ver un ejemplo de estos enfoques en uso. Tal vez echar un vistazo a los siguientes proyectos:

En el caso de NES el repositorio simplemente proporciona una interfaz familiar para agregar y leer agregados directamente desde y hacia la unidad de trabajo.

Algunos enlaces más que podrían ayudar:

 7
Author: Elliot Ritchie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:30:47

No estoy seguro de cuán ortodoxo es esto, pero en un proyecto actual tengo un repositorio para mi raíz de entidad agregada. Este repositorio solo tiene dos métodos, Get y ApplyEvents.

Todos los eventos implementan una interfaz común para su tipo - para pedidos hay OrderEvents, etc. Personalmente pongo la lógica de negocio de cada evento en un método polimórfico, por lo que agregar nuevos tipos de eventos se vuelve muy fácil.

Para Get, el repositorio va a la tienda de eventos, y obtiene todos los eventos en alcance para el tipo(por ejemplo, una sola ubicación de la tienda ordena). Luego hace una repetición de los eventos para llegar a un estado actual de la entidad para todos los eventos que se le da. También puede funcionar a partir de una instantánea, por lo que no estás recreando todos los eventos cada vez que cargues. También puede tener un repositorio de eventos generales para incluso abstraer cómo almacena los eventos y recuperarlos según las especificaciones.

ApplyEvents toma una lista de eventos, y luego cambia el estado de la entidad basado en estos, y lo devuelve. Tenga en cuenta que le está dando al repositorio la opción de recrear la entidad, ¡no solo alterarla! Esto funciona bien con un tipo funcional de programación, pero significa que es mejor evitar la igualdad de objetos (obj1 = = obj2) en C# o Java. Yo diría que solo los objetos de valor, y no las Entidades, deberían tener igualdad de todos modos.

Así es como funciona en la práctica (C#): Tengo pedidos y quiero agregar un artículo. Orden actual.Items está devolviendo una lista vacía. Entonces yo do

Assert.IsFalse(newEvent.Items.Any())
IOrderEvent newEvent = eventFactory.CreateOrderItemEvent(myItemID);
currentOrder = orderRepository.ApplyEvents(currentOrder, newEvent);
Assert.IsTrue(newEvent.Items.Any())

Ahora debería ver currentOrder.Los artículos tienen una entrada.

Las desventajas aquí son que todo mi procesamiento se realiza a través de los eventos, en lugar de tener mi lógica de negocio en la Entidad. Sin embargo, en mi caso, donde casi todos mis objetos necesitan ser serializables (básicamente POCOs) y trabajar en múltiples sistemas, esto realmente funciona bien.

 1
Author: Mathieson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-13 17:57:54