Enfoque DDD para operaciones CRUD básicas en una aplicación compleja centrada en el dominio

9

Mi empresa está reescribiendo nuestra aplicación web desde cero. Es una gran aplicación de nivel empresarial con un dominio complejo en la industria financiera.

Estamos utilizando un ORM (Entity framework) para la persistencia.

En esencia, la mitad de nuestra aplicación se centra en recopilar datos sin procesar del usuario, almacenarlos, y luego la otra mitad de la aplicación que contiene la mayor parte de nuestra lógica de dominio real toma esos datos en bruto para crear nuestra imagen de dominio que difiere mucho de los originales entradas sin procesar, y las pasa a un motor de cálculo, ejecuta cálculos y escupe resultados, que luego se muestran al usuario.

En un enfoque DDD que usa capas, parece que las operaciones CRUD pasan por la capa de dominio. pero al menos en nuestro caso, esto no parece tener sentido.

Cuando un usuario va a la pantalla de edición para cambiar una cuenta de inversión, por ejemplo, los campos en la pantalla son los campos exactos almacenados en la base de datos, no la representación de dominio utilizada más tarde para los cálculos. Entonces, ¿por qué cargaría la representación de dominio de la cuenta de inversión cuando la pantalla de edición necesita la representación de la base de datos (entradas sin formato)?

Después de que el usuario hace clic en "Listo" en la pantalla de la cuenta de inversión, y se realiza una POST al controlador, el controlador ahora tiene una representación de la base de datos de la cuenta de inversión que necesita guardar. Pero por alguna razón, ¿se supone que debo cargar la representación del dominio para realizar modificaciones en lugar de simplemente asignar el modelo del controlador directamente al modelo de la base de datos (modelo de marco de la entidad)?

Entonces, en esencia, estoy asignando un modelo de datos al modelo de dominio, solo para que luego pueda asignarse nuevamente al modelo de datos para persistir. ¿Cómo eso tiene sentido?

wired_in
fuente

Respuestas:

9

Ok, imagine que implementa su página de creación de cuenta asignando la publicación del formulario directamente a un objeto EF que luego se guarda en la base de datos.

Supongamos además que la base de datos tiene varias restricciones que evitan que se ingresen datos completamente incorrectos. Las cuentas siempre tienen clientes, etc.

Todo parece funcionar bien. Pero entonces el negocio establece una nueva regla.

  • Las cuentas creadas los jueves obtienen una tasa de interés adicional del 2%. (suponga que la tasa de interés es uno de los campos de la cuenta)

Ahora tiene que poner esta lógica en algún lugar y no tiene un objeto de dominio para ponerla.

DDD asume que siempre tendrá este tipo de reglas, y probablemente las tenga. La creación de una cuenta debe tener varias comprobaciones, un registro de auditoría, etc.

Planifique su dominio asumiendo que no hay persistencia o controladores MVC con lógica adicional. Asegúrese de capturar todos los requisitos y todos están en el modelo de dominio.

Ewan
fuente
3
Esa es una buena manera de decirlo. Odio encontrar reglas comerciales mezcladas con detalles de DB. +1
candied_orange
Buenos puntos, pero ¿qué pasa si estas reglas de validación solo se aplican durante la creación y actualización de las entradas del usuario? Luego, una vez que tenemos las entradas del usuario, el modelo que se crea al ejecutar los cálculos es un modelo completamente diferente. ¿Deberíamos tener dos modelos de dominio para una cuenta de inversión? ¿Una para operaciones CRUD de entradas sin procesar para el usuario y otra para cuando esas entradas se usan para crear el modelo de dominio utilizado en los cálculos?
wired_in
confundiendo mis preguntas Tendrás que dar un ejemplo completo. Si tiene lógica de dominio, debe ir en un objeto de dominio. Eso no significa que no puedas crear otro objeto de dominio más tarde que el primero
Ewan
Imagine un complejo motor de cálculo. Una de las entradas que necesita para ejecutar los cálculos es una cuenta de inversión, pero toda la cuenta de inversión para el motor de cálculo es un flujo de ingresos durante un período de tiempo. Este modelo de dominio de una cuenta de inversión es completamente diferente de las entradas sin procesar que el usuario ingresó para esta cuenta de inversión. Sin embargo, cuando el usuario ingresa entradas básicas como Nombre, Valor actual, etc., aún debe existir una lógica de validación, pero no debe tener nada que ver con el modelo que utiliza el motor de cálculo. Entonces, ¿hay dos modelos de dominio para una cuenta de inversión aquí?
wired_in
..... o tal vez tener un modelo de cuenta de inversión en el dominio es excesivo para las operaciones CRUD y no solo debería haber alguna validador atributos utilizados o algo
wired_in
7

¿Cómo eso tiene sentido?

Respuesta corta: no lo hace .

Respuesta más larga: los patrones pesados ​​para desarrollar un modelo de dominio no se aplican a aquellas partes de su solución que son solo una base de datos.

Udi Dahan tuvo una observación interesante que puede ayudar a aclarar esto

Dahan considera que un servicio debe tener algún tipo de funcionalidad y algunos datos. Si no tiene datos, entonces es solo una función. Si todo lo que hace es realizar operaciones CRUD en los datos, entonces es la base de datos.

El objetivo del modelo de dominio, después de todo, es garantizar que todas las actualizaciones de los datos mantengan la invariabilidad comercial actual. O, para decirlo de otra manera, el modelo de dominio es responsable de garantizar que la base de datos que actúa como sistema de registro sea ​​correcta.

Cuando se trata de un sistema CRUD, generalmente no es el sistema de registro de los datos. El mundo real es el libro de registro, y su base de datos es solo una representación local almacenada en caché del mundo real.

Por ejemplo, la mayoría de la información que aparece en un perfil de usuario, como una dirección de correo electrónico o un número de identificación emitido por el gobierno, tiene una fuente de verdad que se encuentra fuera de su negocio: es el administrador de correo de otra persona quien asigna y revoca direcciones de correo electrónico, no tu aplicación Es el gobierno el que asigna los SSN, no su aplicación.

Por lo tanto, normalmente no va a realizar ninguna validación de dominio en los datos que le llegan del mundo exterior; es posible que tenga los controles establecidos para asegurar que los datos está bien formado y debidamente desinfectado ; pero no son sus datos: su modelo de dominio no obtiene un veto.

En un enfoque DDD que usa capas, parece que las operaciones CRUD pasan por la capa de dominio. pero al menos en nuestro caso, esto no parece tener sentido.

Eso es correcto para el caso donde la base de datos es el libro de registro .

Ouarzy lo puso de esta manera .

Sin embargo, al trabajar en un montón de código heredado, observo errores comunes para identificar lo que está dentro del dominio y lo que está afuera.

Una aplicación puede considerarse CRUD solo si no hay una lógica de negocios alrededor del modelo de datos. Incluso en este caso (raro), su modelo de datos no es su modelo de dominio. Simplemente significa que, como no hay ninguna lógica de negocios involucrada, no necesitamos ninguna abstracción para administrarla y, por lo tanto, no tenemos un modelo de dominio.

Usamos el modelo de dominio para administrar los datos que pertenecen al dominio; los datos de fuera del dominio ya se administran en otro lugar, solo estamos almacenando una copia en caché.

Greg Young utiliza los sistemas de almacén como una ilustración principal de soluciones donde el libro de registro está en otro lugar (es decir, el piso del almacén). La implementación que describe es muy parecida a la suya: una base de datos lógica para capturar los mensajes recibidos del almacén y luego una base de datos lógica separada que almacena en caché las conclusiones extraídas del análisis de esos mensajes.

Entonces, ¿tal vez tenemos dos contextos limitados aquí? Cada uno con un modelo diferente para uninvestment account

Tal vez. Sería reacio a etiquetarlo como un contexto limitado, porque no está claro qué otro equipaje viene con él. Puede ser que tenga dos contextos, puede ser uno con diferencias sutiles en el lenguaje ubicuo que aún no ha aprendido.

Posible prueba de fuego: cuántos expertos en dominio necesita dos expertos en dominio para cubrir este espectro, o solo uno que hable sobre los componentes de diferentes maneras. Básicamente, puede adivinar cuántos contextos limitados tiene trabajando la ley de Conway al revés.

Si considera que los contextos limitados están alineados con los servicios, puede ser más fácil: ¿debería poder implementar estas dos funciones de manera independiente? Sí sugiere dos contextos limitados; pero si necesitan mantenerse sincronizados, entonces tal vez sea solo uno.

VoiceOfUnreason
fuente
Bueno, hay una lógica de validación y de incumplimiento, pero solo se aplica al crear / actualizar las entradas en bruto para una cuenta de inversión. Luego usamos un modelo mucho más rico para la cuenta de inversión cuando lo usamos como entrada para un motor de cálculo. Entonces, ¿tal vez tenemos dos contextos limitados aquí? Cada uno con un modelo diferente para una cuenta de inversión '
wired_in
Acabo de volver a esto después de varios años, y su comentario está resonando ahora más que antes por alguna razón. Hay muchas cosas buenas aquí, pero ¿podrías aclararme una cosa? Usted dijo: "El objetivo del modelo de dominio, después de todo, es garantizar que todas las actualizaciones de los datos mantengan la invariabilidad comercial actual". Esto se aplicaría a la parte de nuestra aplicación que guarda / actualiza información. La otra parte es solo un motor de cálculo. Toma una representación de los datos como entradas y escupe resultados. ¿No es eso parte del modelo de dominio entonces? ¿Ya que no afecta los datos almacenados?
wired_in
2

En su dominio, no debería tener que saber que la base de datos incluso existe.

Su dominio se trata de reglas comerciales. Lo que necesita para sobrevivir cuando la empresa que creó su base de datos se cierra. Es decir, si quiere que su empresa sobreviva. Es realmente agradable cuando a esas reglas no les importa que hayas cambiado la forma en que persistes los datos.

Los detalles de la base de datos existen y deben tratarse. Deberían vivir en otro lugar. Ponlos a través de un límite. Controle cuidadosamente cómo se comunica a través de ese límite o no es un límite.

El tío Bob tiene esto que decir sobre en qué poner sus datos:

Típicamente, los datos que cruzan los límites son estructuras de datos simples. Puede usar estructuras básicas u objetos simples de transferencia de datos si lo desea. O los datos pueden ser simplemente argumentos en llamadas a funciones. O puede empaquetarlo en un hashmap, o construirlo en un objeto.

Lo importante es que las estructuras de datos simples y aisladas se pasan a través de los límites. No queremos engañar y pasar entidades o filas de bases de datos. No queremos que las estructuras de datos tengan ningún tipo de dependencia que viole la Regla de dependencia.

[...] cuando pasamos datos a través de un límite, siempre es en la forma más conveniente para el círculo interno.

Arquitectura limpia

También explica cómo sus capas externas deben ser complementos de sus capas internas para que las capas internas ni siquiera sepan que existen capas externas.

Hoja de trucos de arquitectura limpia

Siga algo así y tendrá un buen lugar para ignorar la base de datos donde puede preocuparse por las reglas de validación de entrada, las reglas de entrada deben persistirse de alguna manera, las reglas para ejecutar cálculos, las reglas para enviar esos resultados a cualquier salida. En realidad, es más fácil leer este tipo de código.

Es eso o decides que tu dominio es realmente solo para manipular la base de datos. En ese caso, su idioma de dominio es SQL. Si es así, pero no esperes que tu implementación de las reglas comerciales sobreviva a un cambio de persistencia. Terminarás necesitando reescribirlos por completo.

naranja confitada
fuente
Estamos utilizando un ORM (Entity Framework), por lo que nuestra base de datos ya está abstraída, pero los modelos de datos (Entity framework classes) son, naturalmente, casi 1 a 1 con las tablas de la base de datos. El problema es que en algunas partes de nuestra aplicación, el usuario es esencialmente sólo la actualización del modelo de datos (la pantalla es sólo una lista de cuadros de texto, donde cada cuadro de texto es un campo en la base de datos (modelo de datos).
wired_in
Por lo tanto, no veo una razón para no solo usar representaciones de los datos sin procesar (modelo de datos) al realizar operaciones CRUD. Tenemos una representación de dominio compleja utilizada para los cálculos, que es lo que veo como nuestro modelo de dominio, pero no veo por qué cargaría esa imagen en la parte CRUD de nuestra aplicación.
wired_in
Defina lo que quiere decir con "usar representaciones de los datos sin procesar". Los datos se ingresan, los datos se validan de acuerdo con las reglas de dominio, los datos se conservan de alguna manera, los datos se calculan contra, los resultados se envían a lo que sea. ¿Me estoy perdiendo de algo?
candied_orange
Estoy tratando de decir que los datos sin procesar que obtenemos del usuario para una cuenta de inversión no es cómo representamos esa cuenta de inversión en las partes principales de nuestra aplicación, como cuando se usa para cálculos. Por ejemplo, podemos tener una entrada booleana que guardamos en la base de datos llamada IsManagedAccount. El usuario nos proporciona esto a través de un botón de radio en la pantalla de edición. De modo que la representación desde la base de datos hasta la pantalla es de 1 a 1. Cuando construimos nuestro modelo de dominio más adelante en la aplicación, podríamos tener una clase ManagedAccount, por lo tanto, no hay propiedad booleana. Las dos estructuras son muy diferentes.
wired_in
Entonces, cuando el usuario solo está editando las entradas sin procesar en una pantalla de edición, ¿por qué cargaría la imagen del dominio y luego agregaría mucha complejidad para mapear de alguna manera la clase ManagedAccount fuertemente tipada a una representación plana que es solo una clase con una IsManagedAccount? ¿propiedad?
wired_in
1

Aplicando la teoría DDD:

Hay dos contextos limitados en ese dominio:

  • Los cálculos de la cuenta de inversión. El modelo matemático de la cuenta de inversión es un elemento que puede ser un agregado.
  • Core Finance. La cuenta de inversión del cliente es una de las Entidades.

Cada contexto limitado puede tener un diseño arquitectónico diferente.

Ejemplo:

La cuenta de inversión del cliente es una entidad (quizás un agregado, depende del dominio) y la persistencia de los datos se realiza a través del repositorio de la entidad (RDB u otro tipo de base de datos como una base de datos OO).

No existe un enfoque DDD para las operaciones CRUD. Tener un campo de base de datos vinculado a los datos de un objeto rompe los principios de diseño.

Derek
fuente