sourcing net microsoft event español domain-driven-design cqrs ddd-repositories

domain-driven-design - microsoft - cqrs net



¿Cómo encajan los repositorios con CQRS? (2)

He leído sobre los sistemas CQRS que mantienen un simple almacén de valores clave en el lado del comando para representar el estado de una aplicación, y otros que simplemente correlacionan los mensajes (usando algún tipo de saga) y utilizan el almacén de consultas para representar el estado de una aplicación. 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.

Sin embargo, mi experiencia con CQRS solo ha sido con la fuente de eventos, donde hemos reproducido eventos pasados ​​para reconstruir agregados que encapsulan y aplican la lógica de negocios y las invariantes. 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 que se acerque lo más posible al almacén de datos, con esto me refiero a evitar cualquier repositorio, servicio o fachadas, entre su interfaz de usuario (cualquiera que sea) y su almacén de datos.

Podría 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 desde y hacia la unidad de trabajo.

Algunos enlaces más que podrían ayudar:

Según 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". 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 llena con los 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 desea ver la lista de ejecuciones actuales, pregunto en el mismo repositorio y devuelvo un DTO denormalized que representa la información.

Sin embargo, al mirar CQRS, la consulta no afectaría al mismo repositorio. En su lugar, 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 llenaría un objeto de dominio NewRun y ​​luego persistirí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 lo desea) de objetos de dominio?

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

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 del dominio Courier?

ACTUALIZAR

Basándome en algunas lecturas y pensamientos adicionales después del comentario de Dennis, reformularé mis preguntas.

Me parece que CQRS alienta a los repositorios que son simplemente fachadas sobre los mecanismos de acceso y almacenamiento de datos. Dan la "apariencia" de una colección (como describe Fowler) 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 utiliza un UoW para confirmar los cambios realizados en un repositorio (¿verdad?) Pero si el repositorio no mantiene las entidades en la memoria, ¿qué rol tiene un UoW?

Con respecto a una operación de ''escritura'', ¿tendría el controlador de comandos una referencia al mismo repositorio, un repositorio diferente o tal vez un UoW en lugar de un repositorio?


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 tiene solo dos métodos, Get y ApplyEvents.

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

Para Get, el repositorio va al almacén de eventos y obtiene todos los eventos en el ámbito del tipo (por ejemplo, un solo pedido de ubicación de tienda). Luego realiza una repetición de los eventos para llegar al estado actual de la entidad para todos los eventos que se proporcionan. También puede funcionar desde una instantánea, por lo que no está recreando cada evento cada vez que carga. También puede tener un repositorio de eventos general para abstraer incluso de cómo almacenar los eventos y recuperarlos según las especificaciones.

ApplyEvents toma una lista de eventos, y luego cambia el estado de la entidad basándose 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. Argumentaría que solo los ValueObjects, y no las Entidades, deberían tener igualdad de todas formas.

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. 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 negocios en la Entidad. Sin embargo, en mi caso, donde casi todos mis objetos deben ser serializables (básicamente POCO) y trabajar en varios sistemas, esto realmente funciona bien.