2012-04-07 23 views
26

De acuerdo con Fowler (here), un repositorio "media entre el dominio y las capas de mapeo de datos, actuando como una colección de objetos de dominio en memoria". Entonces, por ejemplo, en mi aplicación Courier Service, 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 a la base de datos. Cuando un usuario desea ver la lista de ejecuciones actuales, consulto el mismo repositorio y devuelvo un DTO desnormalizado que representa la información.¿Cómo encajan los repositorios con CQRS?

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 Handler que crearía y poblaría un objeto de dominio NewRun y ​​luego conservaría la información en el almacén de datos.

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

Considere el caso en que la información presentada a mi servicio de aplicación no contiene nada 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 servicio de mensajería asignado a la ejecución. El servicio debe buscar el objeto real de Courier en función del valor de ID y asignar el objeto a NewRun utilizando el método AssignCourier (que valida el servicio de mensajería y realiza otra lógica comercial).

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

ACTUALIZACIÓN

basado en alguna lectura adicional y después pensaron comentario Dennis', voy a reformular mis preguntas.

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

¿Cómo una unidad de trabajo encaja en este enfoque? Normalmente, una UoW se utiliza para confirmar los cambios realizados en un repositorio (¿no?) Pero si el repositorio no mantiene las entidades en la memoria, ¿qué función tiene una UoW?

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

+0

La definición de Fowler difiere ligeramente del patrón de repositorio de Eric Evan como lo describe en el libro DDD. Creo que CQRS se inclina hacia la definición de Evan. –

+0

Por favor explique. La única diferencia que puedo ver es el aspecto "en memoria" pero el uso sería el mismo, ¿verdad? – SonOfPirate

Respuesta

5

He leído acerca de los sistemas CQRS que mantienen un almacén de valores clave simple en el lado del comando 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 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.

Mi experiencia con CQRS siempre ha sido solo con el abastecimiento de eventos, donde hemos reproducido eventos pasados ​​para reconstruir agregados que encapsulan y hacen cumplir la lógica comercial y las invariantes.En este caso, el patrón de repositorio es una abstracción familiar que puede proporcionar una forma más simple de recuperar cualquiera de estos agregados.

En cuanto a la consulta, recomendaría acercarme lo más posible al almacén de datos, con esto quiero evitar cualquier repositorio, servicios o fachadas, etc. entre su UI (cualquiera que sea) y su data store .

Puede ser útil 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 hacia y desde la unidad de trabajo.

Algunos más enlaces que pueden ayudar:

+1

Estoy de acuerdo en que la mayoría de los ejemplos que existen combinan CQRS con Event Sourcing. Sin embargo, tengo más la misma mentalidad que Udi Dahan, quien señala que esta relación es una opción de implementación y no una parte del patrón CQRS. – SonOfPirate

+0

Event Sourcing agrega la idea de que persistimos los cambios como transacciones en lugar de persistir en el estado de nuestras entidades. Puedo ver el valor de eso, pero dado el conocimiento especializado y el proceso de pensamiento que esto requiere, es un enfoque difícil de tomar. Y, en mi caso, estoy lidiando con muchos desarrolladores institucionales y aplicaciones heredadas, incluida una base de datos heredada que debo seguir utilizando. Entonces mi implementación de CQRS no incluye Event Sourcing. – SonOfPirate

+0

Es posible que le resulte difícil mantener su modelo de eventos y el modelo que representa sus aplicaciones estado sincronizado. –

1

No estoy seguro de cómo esto es ortodoxa - pero en un proyecto en el que tiene un repositorio para mi agregada raíz de entidad Este repositorio tiene solo dos métodos, Get y ApplyEvents.

Todos los eventos implementan una interfaz común para su tipo - para pedidos hay OrderEvents, etc. Yo personalmente pongo la lógica comercial de cada evento en un método polimórfico, de modo que agregar nuevos tipos de eventos es muy fácil.

Para Get, el repositorio va al almacén de eventos y obtiene todos los eventos en el alcance para el tipo (por ejemplo, órdenes de ubicación de una tienda). Luego realiza una repetición de los eventos para llegar a un estado actual de la entidad para todos los eventos que se le otorgan. También puede funcionar a partir de una instantánea, por lo que no está recreando cada evento cada vez que carga. También puede tener un repositorio general de eventos para resumir cómo se almacenan los eventos y recuperarlos según las especificaciones.

ApplyEvents toma una lista de eventos, y luego cambia el estado de la entidad en función de estos y la 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. Sin embargo, argumentaría que solo ValueObjects, y no Entities, debería tener la igualdad.

Así es como funciona en la práctica (C#) - Tengo pedidos, y quiero agregar un artículo. currentOrder.Items está devolviendo una lista vacía. Entonces lo hago

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.Items tiene 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 comercial en la Entidad. Sin embargo, en mi caso, donde casi todos mis objetos necesitan ser serializables (básicamente POCO) y funcionan en múltiples sistemas, esto realmente funciona bien.

Cuestiones relacionadas