Diseño impulsado por dominio e interacción entre dominios

10

Soy un novato DDD relativo, pero estoy leyendo cualquier cosa y todo lo que pueda conseguir para hervir y destilar mi conocimiento.

Me encontré con esta pregunta DDD, y una de las respuestas me ha intrigado.

¿DDD limita los contextos y dominios?

En una de las respuestas, el póster da el ejemplo de un sistema de comercio electrónico con productos que se encuentran en al menos 2 dominios:

1) Catálogo de productos 2) Gestión de inventario

De acuerdo, todo tiene sentido, es decir, en su interfaz de comercio electrónico, le interesa mostrar la información del producto y no le interesa la gestión del inventario.

PERO. Es posible que desee mostrar el nivel de inventario en la página web, o puede que desee mostrar el número de edición del inventario en stock (imagine que su inventario es libros, revistas, etc.). Esta información proviene del dominio de inventario.

Entonces, ¿cómo manejarías esto? Te gustaría

a) ¿Cargar tanto el dominio del producto como el agregado del dominio de inventario? b) ¿Conservaría algunas propiedades en su entidad de dominio de Producto para el número en stock y la edición en stock, y luego usaría Eventos de dominio para actualizarlos cuando se actualice la entidad de Inventario?

Una pregunta final. Sé que estamos destinados a olvidar / ignorar la persistencia del dominio y solo pensar en el dominio. Pero solo para pensar esto, en el ejemplo anterior, terminaríamos con potencialmente 2 tablas de base de datos para el catálogo de productos y el inventario de productos. Ahora, ¿usamos el mismo identificador en estos ya que es el mismo producto? O, ¿podríamos usar 1 tabla y 1 fila de tabla para los datos y simplemente asignar los datos relevantes a las propiedades agregadas?

PendorPaul
fuente

Respuestas:

8

Es posible que desee mostrar el nivel de inventario en la página web, o puede que desee mostrar el número de edición del inventario en stock (imagine que su inventario es libros, revistas, etc.). Esta información proviene del dominio de inventario.

Lo principal a tener en cuenta en este momento es que está hablando de una vista, lo que significa que es aceptable usar datos obsoletos.

Dicho esto, no necesita interactuar con los agregados (que son responsables de evitar que los cambios infrinjan la invariante comercial), sino con una representación de una copia reciente del estado del agregado.

Entonces, lo que normalmente esperaría es una consulta ejecutada en el Catálogo de productos, y otra ejecución en el Inventario, y algo para componer los dos en el DTO que necesita para admitir la vista.

¿Cargar el dominio del producto y el agregado del dominio de inventario?

Entonces eso está cerca . No necesitamos cargar los agregados, porque no vamos a cambiar nada. Pero necesitamos su estado; para que podamos cargar eso. Dicho esto, normalmente esperaría que los dos dominios se ejecuten en diferentes procesos. Por lo tanto, estaríamos llamando a ambos, no cargando ambos.

¿Mantendría algunas propiedades en su entidad de dominio de Producto para el número en stock y la edición en stock, y luego usaría Eventos de dominio para actualizarlos cuando se actualice la entidad de Inventario?

"No cruce las corrientes. Sería malo".

Uso de eventos para coordinar información en contextos de dominio: gran idea. Empujar conceptos que pertenecen a un dominio en otro: opuesto a una gran idea, excepto más.

Desea mantener limpios los dominios. Las aplicaciones que interactúan con los dominios, no es tan importante. Entonces, por ejemplo, es razonable que la aplicación Inventario llame a un servicio en la aplicación del producto para consultar algunos conceptos específicos del producto para agregar a una vista. O viceversa.

No sé de ninguna razón por la que una sola aplicación deba restringirse a un solo dominio. Mientras haya una sola fuente de verdad, puede distribuir las transacciones de la forma que desee.

Pero solo para pensar esto, en el ejemplo anterior, terminaríamos con potencialmente 2 tablas de base de datos para el catálogo de productos y el inventario de productos. Ahora, ¿usamos el mismo identificador en estos ya que es el mismo producto?

Esa sería la manera fácil. En términos más amplios, usa el mismo identificador porque la entidad del mundo real es la misma; los dos contextos limitados diferentes modelan esa entidad de manera diferente, pero el modelo no es la entidad del mundo real.

Cuando eso no funciona, necesitará alguna consulta para cerrar la brecha. Creo que la variación más común de esto es que la entidad más nueva conserva la identificación de la entidad más antigua. Verá esto también dentro de un BC: los solicitantes, una vez aprobados, se convierten en clientes. Es un agregado diferente (el estado asociado con un cliente está sujeto a una invariante diferente a la del solicitante); así que si su capa de persistencia está usando secuencias de eventos, la secuencia para el nuevo agregado necesitará un identificador diferente. Entonces habrá un poco de estado en alguna parte que diga "este solicitante se convirtió en este cliente".

O, ¿podríamos usar 1 tabla y 1 fila de tabla para los datos y simplemente asignar los datos relevantes a las propiedades agregadas?

¡YIKES! No, no hagas eso. Está agregando contención de transacciones sin ninguna razón comercial para hacerlo.

VoiceOfUnreason
fuente
He marcado esto como la respuesta, crédito también a @guillaume a continuación, quien también señaló que leer datos para mostrar en una vista no requiere cargar los agregados. Gracias por una respuesta tan larga y detallada. Partiendo del primer enfoque del modelo de datos "tradicional", me ha resultado difícil obligarme a olvidar la capa de persistencia y centrarme en el lenguaje del dominio. Justo cuando creo que lo tengo, leí otro artículo que deslumbra mi comprensión.
PendorPaul 01 de
Acabo de leer este artículo msdn.microsoft.com/en-us/magazine/dn802601.aspx que detalla el uso de Eventos de dominio para duplicar algunos datos entre contextos. El ejemplo es compartir una lista de clientes entre el servicio al cliente y un sistema de procesamiento de pedidos. Esto es duplicar los datos del cliente en el sistema de pedidos y usar Eventos para sincronizar los datos. Esto va en contra de lo que hemos respondido aquí. Seguramente en la muestra vinculada, el contexto del pedido solo necesita un ID de cliente, y eso se puede completar desde la aplicación que tiene acceso al contexto de servicio al cliente.
PendorPaul 01 de
Querrás ser muy preciso en tu pensamiento, aquí. Copiar datos entre sistemas NO es lo mismo que copiar datos entre modelos de dominio. Cada vez que vea "solo lectura [murmullo]", es un gran indicio de que los datos no pertenecen a ese dominio (incluso si la aplicación aún se preocupa por eso).
VoiceOfUnreason
Sí, eso es lo que yo también pensé. El cambio en el proceso de pensamiento es bastante difícil sin los "expertos" que publican artículos que enturbian las aguas. He pasado hoy realmente analizando mi dominio, para tratar de comprender realmente dónde se encuentran mis raíces BC y Aggregate Roots. Estoy prácticamente allí, pero también sé que puedo perfeccionar y refactorizar mi modelo a medida que avanzo. He estado atrapado en el infierno del análisis durante días, me preocupa comenzar a escribir código por temor a no tener DDD directamente en mi cabeza. Creo que es mejor descifrar y seguir cuestionando mi código y si el modelo es correcto. Estaré ahí. Gracias
PendorPaul 01 de
Tal vez leí mal, pero la práctica con la que creo que estás hablando se llama Transferencia de Estado Llevada por Evento (Transferencia de Estado Llevada por Evento) y puede ser un modelo muy poderoso especialmente para vistas de tablero / tabla donde necesitas filtrar y paginar datos a través de los servicios. Puede crear "proyecciones" (básicamente vistas) a partir de eventos de dominio para controlar estos paneles. Lo he usado antes con bastante éxito. Puede ser una herramienta muy poderosa en el escenario correcto. En mi humilde opinión, lo más importante aquí es que te des cuenta de que estas proyecciones no representan modelos de dominio y que no deberían contener lógica empresarial.
Jordania el
3

Creo que su pregunta realmente requiere 2 conjuntos de opciones ortogonales:

  • ¿Carga dos objetos y presenta sus datos juntos o carga 1 objeto que contiene todo lo que desea?

  • ¿Utiliza agregados para mostrar cosas u otra cosa?

Si cree en el enfoque CQRS, resulta que los agregados pueden no ser la mejor opción para las lecturas. Cada vez que carga un agregado, ya sea para mostrar sus datos o modificarlo, agrega concurrencia y contención a su sistema. Además, los agregados son potencialmente más voluminosos y más lentos de cargar que si usa modelos de lectura ad-hoc diseñados para mostrar.

La solución a) de su Q parece estar sujeta a muchas de estas trampas. La opción b) puede ser válida, pero la usaría solo si InventoryManagementse necesitan datos del BC para imponer invariantes al mutar el Productagregado. Es mejor si un agregado contiene todos los datos necesarios para verificar sus reglas de negocio después de la modificación, pero en el lado de lectura pueden ubicarse en cualquier lugar.

Con respecto a los datos, una recomendación común es dar a los Contextos delimitados su propia base de datos (por razones de implementación y SoC). Probablemente tendrá que usar los mismos identificadores si desea hacer coincidir productos entre los dos BC.

Acerca de las interacciones entre BC, también puede consultar /programming/16713041/communicating-between-two-bounded-contexts-in-ddd

guillaume31
fuente
1
OK, eso ayuda. Esencialmente, estamos usando Aggregate Roots y BC para asegurarnos de que nuestros invariantes sean consistentes, y nuestros datos sean válidos, y las operaciones que deseamos realizar estén envueltas dentro de nuestro agregado o BC. Cuando se trata de leer y mostrar esos datos, podemos manejarlos por separado y ligeros sin que todos los agregados / BC estén hidratados. Después de todo, ¿por qué necesitamos cargar el Agregado cuando todo lo que estamos haciendo es mostrar los datos en un informe o en pantalla? Esto tiene mucho sentido. Gracias.
PendorPaul 01 de
Aquí es donde realmente brilla CQRS. Simplemente puede hacer una consulta SQL, unir las tablas y devolver la consulta personalizada para una vista específica. También borra su repositorio del desorden del método de consulta. Además, incluso puede replicar datos en servicios como ElasticSearch y consultar en su contra.
importa el
1

DDD está destinado a aplicaciones donde la lógica de negocios es compleja. "imprimir algo" no es una lógica empresarial compleja. En realidad no es una lógica de negocios en absoluto.

Si la lógica de negocios en un contexto necesita alguna información para manejar adecuadamente algún caso de uso, entonces esa información es parte de ese contexto. Por lo tanto, la idea de que un contexto limitado podría necesitar información disponible en un contexto limitado diferente no tiene sentido, porque el contexto limitado tiene toda la información que necesita.

Eufórico
fuente
Bien, tome algo como Amazon, que es un sistema complejo con una lógica empresarial compleja. Tienen gestión de catálogo y gestión de inventario. No necesitan totales de inventario para administrar el catálogo, me refiero al nombre del producto, la descripción, el tipo, la condición, etc. Sin embargo, muestran la cantidad de artículos en stock en la página principal de su tienda. En ese escenario, donde desea separar sus dominios de Gestión de catálogo de productos e Inventario de productos, pero necesita mostrar información sobre el inventario en la página de su producto, ¿cómo lo hace?
PendorPaul 01 de
Supongo que lo que digo es que el dominio del Catálogo de productos tiene toda la información que necesita para administrar el catálogo de productos. El dominio de inventario tiene toda la información que necesita para administrar el inventario. Sin embargo, cuando quiero mostrar cierta información a un usuario, y esos datos provienen de 2 dominios, ¿dónde lo hago? ¿Acabo de cargar ambos dominios en mi interfaz de usuario y vincular las propiedades que quiero mostrar? ¿O tengo algún mecanismo de informe / lectura donde devuelvo un tipo anónimo o DTO que contiene los datos que necesito para mi interfaz de usuario?
PendorPaul 01 de
Es cómico mirar estos viejos comentarios míos hace 11 meses, pero parece una eternidad. Tenías toda la razón Eufórico sobre el aspecto de leer y escribir. La aplicación en la que estoy trabajando ha evolucionado bastante a medida que mi comprensión ha evolucionado. Ahora estoy usando métodos CQRS, a través de Jimmy Bogards Mediatr. La increíble flexibilidad de tener comandos que interactúan con los agregados, pero luego usar consultas y manejadores de consultas para recuperar todo lo que necesito para mostrar es increíble. Envuelva eso en Vistas que llaman a esos QueryHandlers, y el desacoplamiento es bueno con este. Gracias
PendorPaul
@PendorPaul, creo que estoy donde estabas hace 11 (ahora 13) meses. ¿Qué te llevó a donde estás ahora?
Greg Bell
1
@ GregBell Necesitas forzar un cambio de mentalidad. Estaba atrapado en el enfoque de "diseñar una base de datos, construir un nivel de datos, construir cierta lógica de negocios ... etc.". Y estaba enfocado en crear todas las entidades que lo abarcaban. es decir, un "Producto" en un sitio de comercio electrónico, que manejaba todo, desde precios, descripción, inventario, ubicación de inventario ... pero eso se vuelve extremadamente complejo. El enfoque de contexto acotado significa que en el contexto de Inventario, el "Producto" solo contiene información y comportamiento para la gestión de inventario. La descripción y las imágenes se gestionan en un contexto de contenido, los precios en el contexto de precios.
PendorPaul
1

Desde mi punto de vista, hay diferentes definiciones de "Producto": cada contexto limitante tiene su propia definición de "producto" -dominio:

  • En el Contexto de gestión de contenido, un producto tiene una imagen y un texto descriptivo.
  • En el contexto de inventario-límite, un producto tiene cantidades de existencias, vendedor del producto, pronósticos cuando el producto estará disponible
  • En el contexto de límite de cálculo de precios hay reglas sobre cuánto puede costar un producto por cantidad.

Además de estos, agregaría un Contexto de límites de tienda adicional con su propia definición de producto (una combinación relevante de los dominios de producto de los otros Contextos de límite).

Una Tienda-Producto tendría "imagen y texto descriptivo" del contenido y disponibilidad del "Inventario" pero no "vendedor del producto" del inventario.

Este contexto de tienda adicional depende del contenido, inventario y precio del contexto de tienda.

k3b
fuente
Entonces, ¿cómo estás creando ese Shop-Product BC? Dentro de ese contexto, ¿tiene una referencia al Producto e Inventario BC, y está hidratando los de su tienda de persistencia cuando carga un Producto de Tienda BC, y luego ofrece las propiedades que desea de esos BC a través de las propiedades de su Producto de Tienda? En realidad, encontré este artículo que está en la línea de lo que estaba pensando, cruza los eventos de BC, pero @ guillaume13 arriba ha señalado que, para fines de visualización, puedo evitar el BC y simplemente retirar los datos que necesito para mi vista. msdn.microsoft.com/en-us/magazine/dn802601.aspx
PendorPaul