Diseño impulsado por dominios: una receta para un pragmático


¿Por qué los DDD generalmente se abordan desde el lado equivocado? ¿Y de qué lado quieres? ¿Qué tienen que ver las jirafas y los ornitorrincos con todo esto?

Especialmente para Habr: una transcripción de texto del informe "Diseño impulsado por el dominio: una receta para un pragmático". El informe se realizó en la conferencia DotNext .NET, pero puede ser útil no solo para los donantes, sino para todos los que estén interesados ​​en DDD (creemos que dominará un par de ejemplos de código C #). También se adjunta una grabación de video del informe.



Hola a todos, mi nombre es Alexey Merson. Te diré qué es el diseño impulsado por dominio y cuál es su esencia, pero primero, descubramos por qué es necesario.



Martin Fowler dijo: "Hay pocas cosas que son menos lógicas que la lógica de negocios". La jirafa es definitivamente una de estas pocas. La distancia entre el cerebro y la laringe de una jirafa es de solo unos centímetros. Sin embargo, el nervio que los conecta alcanza los 4 metros. Primero, baja por todo el cuello, allí rodea la arteria y luego regresa casi de la misma manera.

A primera vista, realmente no hay lógica. Pero este es solo un denso legado que quedó de los peces antiguos. En los peces, como saben, no hay cuello, por lo que este nervio recorre el camino óptimo. Y cuando aparecieron los mamíferos después de varios millones de años de refactorización, el nervio tuvo que extenderse para mantener la compatibilidad con versiones anteriores. Bueno, ¿no remodelas por alguna jirafa?

Pero la jirafa está bien, porque hay un ornitorrinco.


Piénsalo bien. El mamifero Con un pico. Vive principalmente en el agua. Pone huevos. Y además, venenoso. Puede parecer que la única explicación lógica de su existencia es que él es de Australia.

Pero creo que todo es más banal. El contratista simplemente se olvidó del diseño y ahorró con StackOverflow, bueno, o lo que había en esos días.

Sé lo que estás pensando ahora: "Alexey, bueno, ¡nos prometiste un diseño impulsado por dominio, y aquí hay una especie de" en el mundo animal "!"

Colegas, ¿qué es el desarrollo? El desarrollo es cuando tomamos parte del mundo real, un proceso de negocios, y lo convertimos en código, es decir, construimos un modelo de software. ¿Qué problemas nos esperan en el camino?

El primero es la complejidad de los procesos comerciales en sí mismos, es decir, la dificultad de comprender cómo funciona el negocio, qué procesos tienen lugar allí, según la lógica con la que se construyen.

El segundo problema es la implementación de estos procesos de negocio en forma de código, el uso de los patrones correctos, los enfoques correctos, etc. Este también es un tema bastante complicado.

Mire, los procesos comerciales son como esa jirafa: comenzaron con los unicelulares más simples, y luego "lo" mira, y nadie entiende "de dónde vino" o "cómo funciona".

Para construir un modelo exitoso de dicho proceso, primero debe responder la pregunta "¿por qué?". ¿Por qué queremos construir este modelo? ¿Qué objetivos queremos alcanzar? Después de todo, si el cliente quería una jirafa de peluche, pero tenía las agallas, se molestaría, incluso si la digestión en este modelo se implementa por el bien de los ojos. Y el cliente perderá no solo dinero y tiempo, perderá la confianza en nosotros como desarrolladores, y nosotros perderemos nuestra reputación y cliente.

Pero incluso si descubrimos los objetivos, esto todavía no garantiza que no obtendremos el ornitorrinco como resultado. El hecho es que el objetivo es poco para entender. El objetivo debe ser alcanzado. Y esto nos ayuda a un diseño basado en dominio.

El objetivo principal del diseño impulsado por dominio es combatir la complejidad de los procesos comerciales y su automatización e implementación en código. "Dominio" se traduce como "dominio", y el desarrollo y el diseño dentro del marco de este enfoque se alejan del dominio.

El diseño controlado por dominio incluye muchas cosas. Esta planificación estratégica, y la interacción entre las personas, los enfoques de la arquitectura y los patrones tácticos, es todo un arsenal que realmente funciona y realmente ayuda a hacer proyectos. Solo hay un "pero". Antes de comenzar a lidiar con la complejidad con el diseño impulsado por dominio, debe aprender a lidiar con la complejidad del diseño impulsado por dominio.



Cuando una persona comienza a sumergirse en este tema, se le cae una gran cantidad de información: libros gruesos, un montón de artículos, patrones, ejemplos. Todo esto es confuso, y es fácil, como dicen, no darse cuenta detrás de los árboles del bosque. Una vez sentí esto en mí mismo, pero hoy quiero compartir mi experiencia con usted y ayudarlo a atravesar esta jungla, finalmente comenzando a usar el diseño impulsado por dominio.

El término diseño impulsado por el dominio fue propuesto por Eric Evans en 2003 en su libro impronunciable, que la comunidad simplemente llama el Libro Azul. El problema es que la primera mitad del libro Evans habla sobre patrones tácticos (todos los conocen: son fábricas, entidades, repositorios, servicios), y las personas generalmente no llegan a la segunda mitad. El hombre mira: todo es familiar, iré a buscar la aplicación DDD.

A la derecha está lo que sucede si lanzas locamente patrones tácticos en el compilador. Izquierda: si usa patrones estratégicos.



Desde el lanzamiento del Libro Azul, se ha formado una comunidad DDD bastante fuerte, se han repensado muchas cosas. Sí, y el propio Evans admitió que ya no comprende cómo podría poner fin a algo tan importante como el diseño estratégico.

Y 10 años después, en 2013, el Libro Rojo fue publicado por Vaughn Vernon. Y en este libro, la presentación ya está construida en el orden correcto: comienza con el diseño estratégico, con lo básico. Y cuando el lector ha recibido la base necesaria, ya comienzan a hablar sobre patrones tácticos y detalles de implementación.

Por lo general, en los informes sobre DDD recomiendan leer Evans, en Internet incluso hay manuales completos en el orden en que debe leer los capítulos para una inmersión adecuada. Recomiendo hacerlo más fácilmente: comience con el Libro Rojo, léalo y luego continúe con Azul.

Y dado que el diseño estratégico es algo tan importante, hablemos de sus ideas clave.

"Ideas clave del diseño estratégico"


En cualquier proyecto de automatización de negocios, siempre hay expertos en dominios. Estas son personas que entienden mejor cómo funcionan los procesos de negocio que se van a modelar. Estos pueden ser desarrolladores líderes, ejecutivos, altos directivos. En general, puede ser cualquiera, si solo él comprende los procesos comerciales con los que tenemos que lidiar.



Por otro lado, hay expertos técnicos: desarrolladores, arquitectos que están directamente involucrados en la automatización e implementación de aplicaciones. En el ejemplo representado, el cliente probablemente quería un ferrocarril para niños, pero resultó ser una especie de monstruo.

¿Por qué está pasando esto? Debido a que la interacción entre expertos técnicos y expertos en dominios en una situación típica se ve más o menos así: hay un muro muy grande entre ellos, y un gerente camina a lo largo de la parte superior de este muro y primero trata de escuchar lo que gritan a un lado del muro, luego trata de gritarlo al mejor de los paquetes al otro lado de la pared, y así sucesivamente en un círculo.

A veces, un gerente es sordo, entonces se puede construir una cadena completa de tales administradores, lo que, por supuesto, no contribuye al éxito del proyecto. ¿Y cómo debería ser?



Debe haber una interacción constante. Expertos técnicos, expertos en dominios: todos los participantes del proyecto deben mantener una comunicación constante, sincronizarse, discutir objetivos, formas de alcanzarlos y por qué hacemos todo esto.

Y aquí llegamos al primer y, probablemente, el punto clave más importante tanto del diseño estratégico como del diseño impulsado por dominio en general.



La comunicación entre los participantes del proyecto forma lo que Domain-Driven Design llama lenguaje ubicuo. Él no es uno en el sentido de que es uno para todas las ocasiones. Justo lo contrario. Es único en el sentido de que todos los participantes se comunican en él, toda la discusión se lleva a cabo en términos de un solo idioma, y ​​todos los artefactos deben ser máximos en términos de un solo idioma, es decir, comenzando desde TK y terminando con un código.

Escenarios de negocios


Para una mayor discusión, necesitamos algún tipo de escenario empresarial. Imaginemos esta situación:



El director del grupo JUG.ru se acerca a nosotros y dice: "Chicos, el flujo de informes está creciendo, la gente, en general, es torturada para hacer todo manualmente ... Automaticemos el proceso de preparación de la conferencia". Respondemos: "¡Está bien!" - y ponte a trabajar.

El primer escenario que automatizaremos es: "El orador presenta una solicitud para un informe en un evento específico y agrega información sobre su informe". ¿Qué vemos en este escenario? Qué es un orador, hay un evento y hay un informe, lo que significa que ya es posible construir el primer modelo de dominio.



Aquí tenemos un modelo de dominio: Altavoz - orador, Talk - informe, Evento - evento. Pero el modelo de dominio no puede ser ilimitado, no puede cubrir todo, de lo contrario se volverá borroso y perderá el foco, por lo que el modelo de dominio debe estar limitado por algo. Este es el siguiente punto clave.



Tanto el modelo de dominio como el lenguaje ubicuo están limitados por el contexto que Domain-Driven Design llama contexto acotado. Restringe el modelo de dominio de tal manera que todos los conceptos dentro de él no sean ambiguos y todos entiendan lo que está en juego.

Si dicen "Usuario", entonces todo debería estar claro a la vez, debería tener un papel comprensible, un significado comprensible, no debería ser algún tipo de usuario abstracto desde el punto de vista de la industria de TI.



En nuestro caso, este modelo de dominio es válido para el contexto de la preparación de la conferencia, por lo que es en el contexto que llamaremos el "contexto de planificación de eventos". Pero para que el orador agregue algo, cambie la información, de alguna manera debe iniciar sesión, debe tener algunos derechos. Y este ya será otro contexto, "Contexto de identidad", en el que habrá algún tipo de sus propias entidades: Usuario, Rol, Perfil.

Y mira qué cosa está aquí. Cuando una persona inicia sesión en el sistema y tiene la intención de ingresar algún tipo de información, físicamente esta es la misma persona, pero en diferentes contextos está representada por diferentes entidades, y estas entidades no están directamente relacionadas.

Si tomamos y, por ejemplo, heredamos el altavoz del usuario, entonces mezclaríamos cosas que no se pueden mezclar, y algunos atributos podrían mezclarse por lógica. Y el modelo perdería el foco en el significado específico que tiene, dividiéndose en varios contextos.

Demostración: servicio de ventas


Pasemos un poco de la teoría seca y miremos el código.

Una conferencia no es solo la preparación de contenido, sino también las ventas. Imaginemos que ya se ha escrito un servicio para vender boletos, y un gerente de ventas se nos acerca y nos dice: “¡Chicos! Una vez que alguien escribió este servicio, averigüémoslo, algo no me queda claro cómo se considera el descuento para clientes habituales ”.

Después de hablar con el gerente, descubrimos que todo el escenario de este servicio es el siguiente: al hacer clic en Pagar, el precio final del boleto se considera teniendo en cuenta el descuento regular para el cliente, y el pedido pasa al estado "Esperando el pago".

El código que ahora analizaremos se puede ver por separado en el repositorio .

Abra la solución, mire la estructura:



Parece que todo se ve bien: hay Aplicación y Núcleo (aparentemente, la gente sabe sobre capas), Repositorio ... Aparentemente, la persona dominó la primera mitad de Evans.

Abra OrderCheckoutService. ¿Qué vemos allí? Aquí está el código :

public void Checkout(long id) { var ord = _ordersRepository.GetOrder(id); var orders = _ordersRepository.GetOrders() .Count(o => o.CustomerId == ord.CustomerId && o.StateId == 3 && o.OrderDate >= DateTime.UtcNow.AddYears(-3)); ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100; ord.StateId = 1; _ordersRepository.SaveOrder(ord); } 


Nos fijamos en la línea con el precio: aquí el precio cambia. Llamamos a nuestro gerente de ventas y le decimos: "Aquí, en resumen, el descuento se considera aquí, todo está claro":

 ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100; 


Él mira sobre su hombro: “¡Oh! ¡Así es como se ve Brainfuck! Y me dijeron que los chicos están escribiendo en C # ".

Obviamente, el desarrollador de este código respondió bien a una entrevista sobre algoritmos y estructuras de datos. Escribí en las olimpiadas de la escuela con el mismo estilo. Después de un tiempo, utilizando el formato y la refactorización obscena , descubrimos qué es qué y le explicamos a nuestro sufrido gerente de ventas que la lógica es esta: si el número de pedidos en los últimos 3 años no es inferior a uno, entonces recibe un 10% de descuento , no menos de tres - 20%, y no menos de cinco - 30%. Se marcha alegre: ahora está claro cómo funciona todo.

Creo que muchos han leído el Código Limpio de Bob Martin. Allí dice sobre la regla de Boy Scout: "El estacionamiento después de que nos vayamos debería estar más limpio de lo que estaba antes de llegar allí". Por lo tanto, refactoricemos este código para que se vea humano y se corresponda con lo que hablamos un poco antes sobre el lenguaje ubicuo y su uso en el código.

Aquí está el código refactorizado.

  public class DiscountCalculator { private readonly IOrdersRepository _ordersRepository; public DiscountCalculator(IOrdersRepository ordersRepository) { _ordersRepository = ordersRepository; } public decimal CalculateDiscountBy(long customerId) { var completedOrdersCount = _ordersRepository.GetLast3YearsCompletedOrdersCountFor(customerId); return DiscountBy(completedOrdersCount); } private decimal DiscountBy(int completedOrdersCount) { if (completedOrdersCount >= 5) return 30; if (completedOrdersCount >= 3) return 20; if (completedOrdersCount >= 1) return 10; return 0; } } 


Lo primero que hacemos es transferir el cálculo de descuento a un Calculador de descuento por separado, en el que aparece el método CalculateDiscountBy customerId. Todo se lee humanamente, todo está claro: qué, por qué y cómo. Dentro de este método, vemos que tenemos globalmente dos pasos para calcular el descuento. Primero: obtenemos algo del repositorio de pedidos, todo depende del caso del usuario, ni siquiera tiene que entrar si esta no es la parte que le interesa ahora. El hecho es que obtenemos el número de algunos pedidos completados, después de lo cual inmediatamente consideramos el segundo descuento para esta cantidad como el segundo paso.

Si queremos ver cómo se considera, vamos a DiscountBy, y aquí casi lo mismo está escrito en inglés casi humano que nuestro "tipo de brainfair" era antes, todo es claro y preciso.

La única pregunta que podría surgir es en qué unidades se mide el descuento. Sería posible agregar la palabra "porcentaje" en el nombre del método para que quede claro, pero por el contexto y las cifras involucradas, lo más probable es que adivine que estos son porcentajes, y por brevedad puede omitirse. Si queremos ver cuál fue el número de pedidos, entonces iremos al código del Repositorio y veremos. Ahora no haremos esto. En nuestro Servicio, necesitamos agregar una nueva dependencia de DiscountCalculator. Y veamos con qué terminamos en la segunda versión del método Checkout.

 public void CheckoutV2(long orderId) { var order = _ordersRepository.GetOrder(orderId); var discount = _discountCalculator.CalculateDiscountBy(order.CustomerId); order.ApplyDiscount(discount); order.State = OrderState.AwaitingPayment; _ordersRepository.SaveOrder(order); } 


Mire, el método Checkout recibe orderId, luego recibe un orderId por orderId, de acuerdo con el CustomerId de este pedido, considera el descuento utilizando la calculadora de descuentos, aplica el descuento al pedido, establece el estado en AwaitingPayment y guarda el pedido. Teníamos un guión en ruso en la diapositiva, pero aquí prácticamente leemos la traducción de este guión al inglés y todo está claro, todo es obvio.

¿Ves lo que es el encanto? Este código se puede mostrar a cualquiera: no solo a los programadores, sino también al control de calidad, a los analistas, a los clientes. Todos entenderán lo que está sucediendo, porque todo está escrito en lenguaje humano. Utilizo esto en nuestro proyecto, realmente QA puede ver algunas piezas, consultar con Wiki y comprender que hay algún tipo de error. Porque el Wiki lo dice, y el código es un poco diferente, pero entiende lo que está sucediendo allí, aunque no es un desarrollador. Y de la misma manera, podemos analizar el código con el analista y analizarlo en detalle. Yo digo: "Mira, así es como funciona en código". Nuestro último recurso no es el Wiki, sino el código. Todo funciona tal como está escrito en el código. Es muy importante utilizar un lenguaje ubicuo al escribir código.



Este es el tercer punto clave.

Hay tanta confusión sobre el diseño impulsado por dominio en cosas como dominio, subdominio, contexto acotado, cómo se relacionan con lo que significan. Parece que todo el mundo está limitando algo, todos de alguna manera están ordenados. Pero no está claro cuál es la diferencia, por qué son tan diferentes inventados.



El dominio es algo global, es un área temática global en la que este negocio en particular gana dinero. Por ejemplo, para DotNext esta es una conferencia, para Pyaterochka es una venta minorista de bienes.

Las grandes corporaciones pueden tener varios dominios. Por ejemplo, Amazon se dedica tanto a la venta de bienes a través de Internet como a la provisión de servicios en la nube, estas son diferentes áreas temáticas.

Sin embargo, es algo global y no puede automatizarse directamente, incluso investigarlo es difícil. Para el análisis, el dominio se divide inevitablemente en subdominios, es decir, en subdominios.



Los subdominios son partes de un negocio que, en nuestro idioma, están altamente conectados, es decir, son algún tipo de procesos lógicos aislados que interactúan entre sí en algún nivel importante.

Por ejemplo, si tomamos una tienda en línea, será la formación y el procesamiento de pedidos, será la entrega, esto es trabajo con proveedores, esto es marketing, esto es contabilidad. Estas son algunas de estas piezas: en esto se divide el negocio.



Desde el punto de vista de DDD, los subdominios se dividen en tres tipos. Y aquí quiero decir una cosa más: a menudo en libros y artículos, el Subdominio simplemente se reduce a Dominio, pero generalmente en el caso cuando se combina con el tipo de Subdominio. Es decir, cuando dicen "Dominio principal", se refieren a Subdominio principal, no se confundan en esto. Al principio me dejó alucinado.

Los subdominios se dividen en tres tipos.



El primero y más importante es Core. Core es el subdominio principal, esta es la ventaja competitiva de la compañía, lo que le hace ganar dinero a la compañía, cómo se diferencia de sus competidores, su know-how, como se llame. Si tomamos la conferencia DotNext, entonces este es el contenido. Todos vinieron aquí por contenido, si no hubiera tal contenido aquí, no irían o irían a otra conferencia. No habría DotNext en la forma en que está.



El segundo tipo es Subdominio de soporte. Esto también es algo importante para ganar dinero, también es algo sin lo cual es imposible, pero no es algún tipo de conocimiento, una verdadera ventaja competitiva. Esto es lo que admite el subdominio principal. Desde el punto de vista de la aplicación del diseño impulsado por dominio, esto significa que se gasta menos esfuerzo en el subdominio de soporte, todas las fuerzas principales se lanzan en Core.

Un ejemplo para el mismo DotNext es el marketing. Es imposible sin marketing, de lo contrario, nadie habría sabido sobre la conferencia, pero sin marketing de contenido no es necesario.



Y finalmente, el subdominio genérico. Genérico es una tarea empresarial típica, que, por regla general, puede automatizarse con productos terminados o externalizarse. Esto es lo que también se necesita, pero no necesariamente requiere una implementación independiente por nuestra parte, e incluso más que eso, generalmente será una buena idea usar un producto de terceros.

Por ejemplo, venta de boletos. DotNext vende boletos a través de TimePad. TimePad automatiza perfectamente este subdominio, y no necesita escribir un segundo TimePad usted mismo.



Y finalmente, contexto acotado. El contexto limitado y el subdominio siempre están cerca, pero hay una diferencia significativa entre ellos. Esto es muy importante



Hay una pregunta en StackExchange sobre cómo el contexto acotado difiere del Subdominio. El subdominio es una parte del negocio, una parte del mundo real, es el concepto de un espacio de declaración de problemas. El contexto limitado limita el modelo de dominio y el lenguaje ubicuo, es decir, cuál es el resultado del modelado y, en consecuencia, el contexto limitado es el concepto de un espacio de solución. En el proceso de implementación del proyecto, se realiza un tipo de mapeo de Subdominios en contextos limitados.



Un ejemplo clásico: la contabilidad como un Subdominio, cómo se mapea el proceso, está automatizado, por ejemplo, 1C Contabilidad, Elba o "Mi negocio" - de alguna manera está automatizado por algún producto. Este es el contexto limitado de la contabilidad, en el que existe su lenguaje ubicuo, su propia terminología. Esa es la diferencia entre ellos.



Si volvemos a DotNext, entonces, como dije, los tickets se asignan a TimePad, y el contenido que es nuestro Subdominio principal se asigna a una aplicación personalizada que estamos desarrollando para la administración de contenido.

Tamaño de contexto acotado


Hay un momento que plantea muchas preguntas. ¿Cómo elegir el tamaño correcto para el contexto acotado? En los libros, uno puede encontrar esa definición: "El contexto limitado debe ser exactamente tal que el lenguaje ubicuo sea completo, consistente, inequívoco, inequívoco, consistente". Definición genial, al estilo de un matemático de una broma famosa: muy precisa, pero inútil.

Analicemos cómo entendemos lo mismo: si debería ser una Solución, un Proyecto o un espacio de nombres, ¿qué escala debería adjuntarse al contexto acotado?



Lo primero que puede leer en casi todas partes: idealmente, un subdominio debe mapearse a un contexto limitado , es decir, ser automatizado por un contexto limitado. Suena lógico, porque tanto allí como existen limitaciones de un proceso comercial separado, en ambos casos, algunos términos comerciales, aparece un solo idioma. Pero aquí debe comprender que esta es una situación ideal, no necesariamente tendrá esto, y no es necesario tratar de lograrlo.

Porque, por un lado, el Subdominio puede ser bastante grande, y se pueden obtener varias aplicaciones o servicios que lo automatizarán, por lo que puede resultar que varios contextos limitados correspondan a un Subdominio.

Pero hay una situación inversa, como regla, esto es típico de Legacy. Es decir, cuando crearon una aplicación muy grande que automatiza todo en el mundo en esta empresa, todo lo contrario resultará. Una aplicación es un contexto limitado, es probable que el modelo sea algún tipo de ambigüedad, pero los Subdominios no han desaparecido de este, respectivamente, un contexto limitado corresponderá a varios Subdominios.

Cuando la arquitectura de microservicios se puso de moda, apareció otra recomendación (aunque no se contradicen entre sí): un contexto limitado por microservicio . Nuevamente, suena lógico, la gente realmente hace eso. Porque el microservicio debe asumir una función clara, que internamente tiene una alta conectividad, y se comunica con otros servicios a través de algún tipo de interacción. Si usa una arquitectura de microservicio, puede tomar esta recomendación usted mismo.

Pero eso no es todo. Permítame recordarle una vez más que el diseño impulsado por el dominio es mucho: sobre el lenguaje, sobre las personas. Y no puede ignorar a las personas y solo hacer criterios técnicos en este asunto. Por lo tanto, escribí esto: un contexto es igual a X-man . Solía ​​pensar que x es aproximadamente 10, pero hablamos un poco con Igor Labutin ( twitter.com/ilabutin ) y la pregunta permaneció abierta.

Aquí es importante entender esto: un solo idioma permanece unificado mientras todos los participantes hablan, discuten y todos lo entienden sin ambigüedades. Está claro que un número infinito de personas no puede hablar el mismo idioma. Nuestra historia de la humanidad muestra claramente esto. En cualquier caso, aparecen algunos dialectos, algunos de sus significados, ahora incluso puedes agregar memes, etc. De una forma u otra, el idioma se volverá borroso.

Por lo tanto, debe entenderse que el número de personas que usan este lenguaje único y, en consecuencia, participan en el desarrollo, en la automatización, es limitado. Los libros también hablan de algunas razones políticas: si dos equipos trabajan bajo el liderazgo de diferentes gerentes y trabajan en el mismo contexto acotado, y por alguna razón estos gerentes no son amigos entre sí, los conflictos comenzarán y se perderá el enfoque. Por lo tanto, será mucho más simple y correcto hacer dos contextos limitados para cada comando y no intentar combinar lo que no se combina.

Arquitectura y gestión de dependencias


Desde el punto de vista del diseño impulsado por dominio, realmente no importa qué arquitectura elija. El diseño dirigido por el dominio no se trata de eso; el diseño dirigido por el dominio se trata del lenguaje y la comunicación.



Pero hay un punto importante, desde el punto de vista de los criterios para elegir la arquitectura que nos interesa desde la perspectiva del diseño impulsado por dominio: nuestro objetivo es eliminar al máximo la lógica empresarial de las dependencias de terceros . Porque, tan pronto como aparecen las dependencias de terceros, aparece la terminología, aparecen palabras que no entran en un solo idioma y comienzan a ensuciar nuestra lógica comercial.



Veamos un ejemplo clásico de arquitectura: la conocida arquitectura de tres capas. Tan pronto como no llamen una capa de dominio (aquí la capa Business): business, core y domain son todos iguales. En cualquier caso, esta es la capa en la que se encuentra la lógica de negocios, y si depende de la capa de datos, significa que algunos conceptos de la capa de datos fluirán de alguna manera en la capa de dominio y la ensuciarán.



La arquitectura de cuatro capas es esencialmente la misma, la capa de dominio aún depende, y como depende, las dependencias innecesarias de terceros llegarán a ella.



Y en este sentido, hay una arquitectura que permite evitar esto: es la arquitectura de cebolla ("cebolla"). Su diferencia es que consiste en capas concéntricas, las dependencias van del exterior al centro. Es decir, la capa externa puede depender de las internas, la capa interna no puede depender de las externas.

La capa más externa es la interfaz de usuario en un sentido global (es decir, no es necesariamente una interfaz de usuario humana, puede ser una API REST ni nada). Y la infraestructura, que a menudo en general también se parece a E / S, es la misma base de datos, de hecho, una capa de datos. Todas estas cosas están en la capa externa. Es decir, debido a que la aplicación de alguna manera recibe algunos datos, comandos, etc., se elimina y la capa de dominio elimina la dependencia de estas cosas.

Luego viene la capa de aplicación, un tema bastante holístico, pero esta es la capa en la que se encuentran los guiones, los casos de los usuarios. Esta capa usa la capa de dominio para implementar sus conceptos.

En el centro está la capa de dominio. Como vemos, ya no depende de nada; se convierte en una cosa en sí mismo. Y es por eso que la capa de dominio a menudo se llama "Core", porque es el núcleo, es lo que está en el centro, lo que no depende de cosas de terceros.



Una de las opciones para implementar dicha arquitectura de cebolla es la arquitectura hexagonal, o "puertos y adaptadores". Traje esta foto para intimidar, no voy a hablar de eso. Al final de la publicación hay un enlace a uno de un millón de artículos sobre esta arquitectura, puede leer.

Un poco sobre patrones tácticos: interfaz separada


Como dije, en primer lugar, la mayoría de los patrones tácticos son familiares para todos, y en segundo lugar, el objetivo de mi informe es que no son la esencia. Pero me gusta el patrón de Interfaz separada por separado, y quiero hablar sobre él por separado.

Volvamos al código de nuestro microservicio y veamos qué pasó con el repositorio.



La capa de dominio tenía la interfaz del repositorio IOrdersRepository.cs y su implementación, OrdersRepository.cs.

 using System.Linq; namespace DotNext.Sales.Core { public interface IOrdersRepository { Order GetOrder(long id); void SaveOrder(Order order); IQueryable<Order> GetOrders(); #region V2 int GetLast3YearsCompletedOrdersCountFor(long customerId); #endregion } } 


Aquí hemos agregado un cierto método para recibir pedidos durante los últimos tres años GetLast3YearsCompletedOrdersCountFor.

Y lo implementaron de alguna forma (en este caso, a través del Entity Framework, pero puede ser cualquier cosa):

  public int GetLast3YearsCompletedOrdersCountFor(long customerId) { var threeYearsAgo = DateTime.UtcNow.AddYears(-3); return _dbContext.Orders .Count(o => o.CustomerId == customerId && o.State == OrderState.Completed && o.OrderDate >= threeYearsAgo); } 


Mira cuál es el problema. El repositorio terminó en la capa de dominio, su implementación en la capa de dominio, pero el código, que comienza con DateTime.UtcNow.AddYears (-3), inherentemente no pertenece a la capa de dominio, y no es una lógica de negocios. Sí, LINQ lo hace más o menos humanizado, pero si, por ejemplo, hubo consultas SQL aquí, todo sería completamente triste.

El significado del patrón de interfaz separada es que la interfaz de servicio que utilizamos en la lógica de dominio se declara en la capa de dominio. Estamos hablando de repositorios y servicios similares en los que los detalles de la implementación de estos servicios no son lógica empresarial. La lógica de negocios es el hecho de la existencia de estos servicios y el hecho de su llamado y uso en la capa de dominio. Por lo tanto, la interfaz del repositorio permanece en la capa de dominio y la implementación se mueve a la capa de infraestructura.

Preparé otra opción. La interfaz del repositorio permanece en el ensamblaje Core, pero la implementación se traslada a Infrastructure.EF.



Por lo tanto, trajimos a la infraestructura aquellos conceptos que no eran peculiares de la capa de dominio. Como efecto secundario, podemos reemplazar esta infraestructura con alguna otra implementación. Pero este no es el objetivo principal, el objetivo principal es, como dije, eliminar la lógica del dominio de las dependencias de terceros.

Una vez más sobre el idioma.


Hablemos una y otra vez sobre el idioma.

Al principio, creamos el modelo de dominio "orador - hablar - evento". Creo que nadie ha planteado ninguna pregunta especial.

Y aquí está el escenario en base al cual construimos este modelo de dominio:



Mira, el guión está en ruso y el modelo de dominio está en inglés.

Para los desarrolladores que no hablan inglés, esto es algo con lo que tienes que vivir constantemente.



Cada uno de ustedes, muy probablemente, realiza constantemente este proceso: traduce del ruso al inglés y viceversa. Aquellos que trabajan con clientes y proyectos de habla inglesa son un poco más fáciles, porque los requisitos están en inglés, las discusiones con los clientes en inglés, por regla general, todos los escenarios están en inglés, el código está en inglés y solo hay comunicación dentro del equipo en ruso, que crece rápidamente en inglés (cliente - cliente, pedido - pedido). Y esa carga cognitiva, esa sobrecarga, creada por una traducción constante, retrocede un poco.

, , , . , .

1, . , — , , .



1. PascalCase , , , , , , , - - .

, - ?



, use case, , . , . C#, , , . , , , .

, , Domain-Driven Design. , , , , C# . .

, - Continuous Integration. , , , - - . , - , , . , 95% , , Continuous Integration, , TeamCity . .

, . . , 1-, , . «» , . , , .



, Domain-Driven Design.

— , . Domain-Driven Design — , . — , , ubiquitous language. , , . , , , .

. , . - , , , , , , , , , , — . .

. . . . , , . DSL-. .NET-, - , , , , . , .

, - . , , ubiquitous language -. .



, , GitHub .

DotNext:
, « , ». DotNext ( 15-16 ) . , 1 , .

Source: https://habr.com/ru/post/440772/


All Articles