Libro de cocina del desarrollador: recetas de diseño impulsado por dominio (Parte 2, estructura e interacción)

ddd-header


Introduccion


En el primer artículo, identificamos el alcance de las prácticas indicadas, para qué proyectos se pueden aplicar y para cuáles no.


En este artículo, me gustaría dar una breve descripción de los principios básicos de DDD, así como compartir mi experiencia personal con su aplicación. Hablaremos con más detalle sobre la comunicación y los enfoques estructurales con ejemplos de su implementación.


En el siguiente artículo escribiré las posibles combinaciones de los patrones de diseño aplicados teniendo en cuenta su implementación, y finalmente daré un ejemplo de una implementación específica de un pequeño microservicio.


DDD


Recordemos la definición anterior:


El diseño impulsado por dominio (DDD) es un enfoque para el desarrollo de software para la satisfacción integral de las necesidades, al vincular estrechamente la implementación con los principales modelos de negocio que están en constante desarrollo.

El libro de referencia que describe la práctica de construir sistemas complejos es Eric Ded ' Domain Driven Dedign (Big Blue Book). Si lees algún artículo de revisión sobre este tema, ya lo sabes. Para cuando use DDD en la práctica, tendrá que leerlo. Este no es el libro más fácil de leer:


La fuente canónica para DDD es el libro de Eric Evans. No es la lectura más fácil en la literatura de software, pero es uno de esos libros que paga ampliamente una inversión sustancial.

Martin Fowler: 15 de enero de 2014

Si se desplaza por el contenido del libro, le parecerá que no está completamente estructurado. Pero el mapa nos ayudará.
DDD-map


En el mapa están aquellas prácticas que consideraremos hoy.


El alcance de las prácticas cubiertas en el libro es enorme. El alcance de las prácticas que se pueden aplicar fuera de este libro es aún mayor. Antes de poner en servicio al menos parte de ellos, identifique sus objetivos. Daré el mío como ejemplo.


  • Aumenta la productividad.
  • Escribe un código comprensible.
  • Escalado a nivel de desarrollo de software.

Idioma único


El desarrollo de software rara vez conduce a la creación de algo nuevo, como regla, esto es una simulación de algo existente.


Un modelo es una representación de un objeto real, que incluye solo las propiedades y funciones necesarias.

No podemos crear un producto de software que cubra todo el área temática. Es posible reproducir solo esa parte que reproducirá la funcionalidad necesaria.


Un buen ejemplo de un modelo sería un mapa topográfico. Ella es una modelo de terreno. El mapa no contiene praderas de campos y ríos, solo refleja la ubicación de objetos reales entre sí.


Para crear un modelo claro y claro para todos, debe hablar el mismo idioma. No solo Eric Evans nos dice esto, sino también el sentido común. Si los programadores usan sus términos y hacen su propia "jerga", entonces los primeros simplemente no entenderán lo que hay que hacer. Las empresas en este caso no podrán darse cuenta del costo real de desarrollar una u otra "característica". ¿Cuántas veces has escuchado: "Sí, es solo un botón para agregar"?


Su objetivo como diseñador de sistemas debe ser obtener la máxima comprensión mutua de todo el equipo. ¿Cómo lograr esto? Comienza a hablar Si las personas comienzan a comunicarse en cualquier grupo cercano, tienen un cierto conjunto de términos generalmente aceptados. En diferentes empresas, el proceso de introducción de un lenguaje común es probable que sea diferente. Esto puede ser una decisión decidida o un procedimiento democrático. El idioma se puede indicar explícitamente, y no se ingresa explícitamente, en cuyo caso simplemente comienzan a hablarlo. Una buena técnica para introducir un lenguaje común es la documentación general.


Cómo mantener la documentación del proyecto


  1. Cualquier comunicación entre negocios y desarrollo debería mejorar su modelo.
  2. Después de la reunión, registre el resultado en forma de documentación (artefacto Scrum) y muestre esta documentación a todos los participantes en el proceso de desarrollo.
  3. Use un solo idioma en la documentación.
  4. Lo más importante: no pierda el tiempo en la documentación. Todavía tiene que escribir código, y la documentación se reescribirá muchas veces, gastar recursos es costoso. En lugar de perder el tiempo con la aplicación de gráficos UML durante mucho tiempo, use una servilleta, un bolígrafo y una cámara en su teléfono.
  5. La documentación requiere disciplina; no puede escribirla de vez en cuando.
  6. Separar la documentación:
    • Comentarios en el código: describa momentos incomprensibles directamente en el código, deje #ODO: (elimine al fusionar el código en maestro). Exprese su opinión en los comentarios, por ejemplo, debe usar una u otra muleta cuando trabaje con código legasy.
    • Los comentarios sobre el proyecto README.md en el directorio raíz de su proyecto deben contener información técnica: cómo iniciar el proyecto, cómo ejecutar pruebas, etc. También es una buena idea obtener un mapa donde tenga todos los proyectos y en qué servidores se ejecutan. Registre por separado todos los acuerdos aceptados.
    • Y lo más importante, la base de conocimiento. Una colección de documentos que describen los procesos comerciales, esta es la parte de los documentos que está disponible tanto para usted como para la empresa.
  7. El principal error de quienes escriben documentación es la redundancia. No intentes cubrir todo y todo, transmitir solo el significado general. La documentación debe complementar su proyecto, pero no reemplazarlo de ninguna manera. No escriba todos los términos que solo sean ambiguos. Si una definición toma más de dos oraciones, es una definición pobre.

Documentación de ejemplo:


 #         . # :  : -    - email -  ## : ###        ,     email  ,    1  (      email  ). ###           .          email . ###  email  email     .    ,    email.   . ###       ,     ,    .   . ###      email,    ,       .   2 . ###                  .    ,     ,             . 

Tenga en cuenta que en este ejemplo no especificamos un diccionario explícito, sin embargo, arreglamos el concepto de Usuario , Autorización , Registro . Escribir dicha documentación no le llevará al experto más de 20 minutos.


Para una persona que no es experta en el área temática, el proceso de escribir documentación se percibe como algo complicado. Es necesario separar la recopilación de conocimientos y el registro de los conocimientos recopilados. != + .


"Lo que llamas el universo", dijo el cuarto, "es, de hecho, una acumulación de mundos que, como la piel de un arco, están uno encima del otro y se separan gradualmente uno del otro".

- Inusualmente claramente indicado! - Admiré a los abderitas. - Increíblemente claro! "Pensaron que entendían al filósofo, porque sabían muy bien qué era la cebolla".

Historia de Aberdeen, Cristov Martin Wieland

Contextos y dominios limitados


Imagine que actuamos como diseñadores de una startup progresiva. A todos nos encanta la pizza fría, maldecir con correos y completar formularios en el sitio durante horas. Por lo tanto, se nos ocurrió una maravillosa startup "Cuatro tortugas y una rata":


  • Hay un sitio en el que las pizzerías se registran y publican sus platos reales.
  • Estas pizzerías no tienen su propio servicio de mensajería.
  • Hay clientes que no pueden comunicarse con la pizzería, pero están listos para hacer un pedido a través del sitio web o la aplicación móvil.
  • 'Característica' asesina: los correos no son personal especialmente contratado, sino personas comunes que se registraron a través de la aplicación móvil
  • Los correos reciben órdenes, después de su ejecución reciben el pago por el trabajo realizado.
  • Lleva más tiempo esperar a tales correos, pero la entrega y, en consecuencia, la pizza, es más barata.

Recordemos la documentación que describimos en el capítulo anterior. Allí, en nuestro diccionario único, se utilizó el término registro . Pero en este proyecto tenemos varios de ellos:


  • Registro de clientes
  • Registro de pizzería
  • Registro de mensajería
  • Registro de pedidos

Un lenguaje unificado pertenece a un contexto limitado. El dominio de la documentación anterior es 'Sistema de autorización'. Intentemos asignar dominios para nuestro inicio.


Pero antes de comenzar, veamos una pequeña terminología sobre qué es un dominio y qué contexto es limitado.


Dominio (Dominio): una representación de una estructura comercial real que resuelve un problema específico.

Por ejemplo: sistema logístico, sistema de pago, sistema de autorización, sistema de gestión de pedidos.


El dominio se divide en subdominios, que describen estructuras más pequeñas, por ejemplo: una cesta de pedidos, un sistema para construir rutas.


Cada dominio tiene un área de responsabilidad limitada: funcionalidad limitada.


Contexto limitado: un conjunto de restricciones de dominio que ayuda al dominio a centrarse en una sola tarea para su mejor solución.

Me gusta presentar este término como tal abstracción. Un dominio es un círculo. Un contexto restringido es un círculo.


Contexto de dominio


Incluso en la terminología DDD, se asigna el núcleo.


El núcleo (dominio principal): el dominio más importante que más caracteriza a su negocio.

Entonces, los dominios del proyecto "Cuatro tortugas y una rata":


Trabaja con una pizzería (Pizzarias)


Contexto : b2b todo lo relacionado con pizzerías


Subdominios :


  • registro de nuevas pizzerías
  • agregando surtido
  • actualizar el estado de la disponibilidad de un producto

Trabajar con el cliente (clientes)


Contexto : b2c, todo lo relacionado con trabajar con clientes de pizza


Subdominios :


  • ver surtido
  • materiales informativos

Trabajar con mensajeros (sistema de entrega)


Contexto : b2e, todo lo relacionado con el trabajo con correos


Subdominios :


  • registro de mensajería
  • asignación de tareas
  • registro de solicitudes de retiro de fondos ganados por el servicio de mensajería.

Sistema de pedidos


Contexto : Kernel. Le permite coordinar todos los dominios individuales, proporcionando un ciclo completo desde la recepción de un pedido hasta la entrega de pizza al usuario. No es un artista intérprete o ejecutante, pero desempeña el papel de conductor.


Subdominios :


  • aceptación del pedido
  • ejecución de orden
  • seguimiento del estado del pedido

Sistema de liquidación (facturación)


Contexto : contiene todas las transacciones financieras. Proporciona interacción con el centro de procesamiento.


Subdominios :


  • aceptar dinero para pedidos
  • dar dinero a los mensajeros por el trabajo realizado

Sistema de estadísticas


Contexto : La recopilación y procesamiento (no emisión) de información analítica.


Subdominios :


  • estadísticas sobre fondos
  • estadísticas de la aplicación

Sistema de gestión (panel de gestión )


Contexto : la emisión de información analítica. Kit de herramientas de decisiones de gestión.


  • análisis basados ​​en estadísticas recopiladas
  • moderación previa de pagos a correos

Según los dominios, vamos a mapearlo.


Un mapa de dominio (mapa de contexto) es una herramienta gráfica que le permite describir las relaciones entre dominios individuales.

Mapa de contexto


El mapa muestra enlaces entre dominios. Este mapa es muy superficial, pero el área temática no se comprende bien. Este es el primer boceto, reescribiendo el cual obtendrá el resultado esperado.


Lo más importante en el mapa es que vemos conexiones entre dominios. Tal estructura encaja muy bien en la arquitectura de microservicios:


El principio principal de la arquitectura de microservicios: conectividad débil y fuerte adhesión.

Este principio es dado en el libro de Sam Newman - Creación de microservicios , este es el segundo libro que tendrá que leer para comenzar a usar los enfoques descritos en este artículo. Lo que se quiere decir: los dominios deben estar libremente acoplados, pero estrechamente vinculados internamente.


La traducción de estos términos se toma de la traducción oficial al ruso y, tal vez, refleja mal el significado transmitido. En los términos originales son: bajo acoplamiento (conectividad, compromiso, agarre, conjugación), alta cohesión (conectividad, resistencia).


Práctica de implementación de dominio compartido


Me gustaría compartir mi experiencia personal: un conjunto de decisiones informadas. No te insto a que uses estas soluciones. Pero pueden ser una buena opción si no sabe por dónde empezar. Con experiencia personal, las herramientas se adaptarán aún más a sus necesidades.


Los principios principales que nos guiaron:


  • La simplicidad de la solución. Haga las cosas complejas simples, no simples complicadas.
  • Pragmatismo Siempre debe observar la situación y, en ausencia de una solución existente, desarrollar una nueva. Intenta tipificar todo, pero evita el dogmatismo.
  • Código! = Documentación. El código son instrucciones para la máquina, la documentación es una instrucción para las personas. No hay necesidad de confundirlos y dar uno tras otro.
  • SÓLIDO

¿Cómo implementar dominios?


Es muy conveniente seleccionar dominios como microservicios separados.


Microservices es una aplicación separada que implementa la lógica de un dominio.

En el desarrollo DDD, el principio de asignar un microservicio en una aplicación separada será un contexto limitado. Esto no niega el principio técnico de separación de servicios (si esto se debe a la necesidad de garantizar un alto rendimiento). Pero el principio contextual será dominante y vinculante.


¿Cómo resaltar la relación entre dominios?


Las relaciones entre dominios siempre son una API. Podría ser RESTful json api, gRPC, AMPQ. En el marco de este artículo, no compararemos un protocolo con otro y destacaremos sus ventajas y desventajas; cada uno de ellos tiene su propio campo de aplicación. Pero aún así, detengámonos en recomendaciones generales:


Sea flexible al elegir un protocolo y rígido en la uniformidad de su implementación.


Elija un protocolo para cada par de dominios individualmente, no intente usar http en todas partes, puede necesitar colas asincrónicas en algún lugar y las ventajas de AMPQ le resultarán obvias. No ignore esta oportunidad porque tiene RESTful en todas partes.


Por otro lado, si está implementando RESTful json, use un estándar de estructuración de datos. Puedes tener listo por ejemplo jsonapi u openapi. Si por alguna razón, las soluciones preparadas no le convienen y usted siente que puede desarrollar su estándar, describirlo y usarlo. Pero úselo en todas partes, no cree estándares de "zoológico". Si necesita comunicarse con un sistema externo donde no saben nada sobre sus estándares, escriba un adaptador de microservicio.


Adaptador


¿Cómo implementar subdominios?


Como módulos separados dentro de un microservicio.


Un módulo es una implementación de un subdominio, al poner la lógica en un espacio de nombres separado (espacio de nombres) dentro de un único microservicio.

¿Cómo se ve todo esto? Veamos un ejemplo. Como recordamos, tenemos un dominio del sistema de entrega: este dominio tiene tres subdominios:


  • registro de mensajería
  • cuestión de tareas (tareas)
  • registro de solicitudes para el retiro de fondos ganados por el servicio de mensajería (retiro)
  • comprobar que su microservicio funciona, herramienta auxiliar, técnica (healt_checker)

Imagina todo esto en forma de una estructura de carpetas:


 $ tree --dirsfirst delivery_system delivery_system ├── app/ │ ├── health_checker/ │ │ └── endpoints.rb │ ├── registrations/ │ │ ├── entities/ │ │ ├── forms/ │ │ ├── repositories/ │ │ ├── interactor/ │ │ ├── services/ │ │ ├── validations/ │ │ ├── endpoints.rb │ │ └── helpers.rb │ ├── tasks │ │ ├── entities/ │ │ ├── queries/ │ │ ├── repositories/ │ │ ├── endpoints.rb │ │ └── helpers.rb │ └── withdrawals │ ├── entities/ │ ├── forms/ │ ├── repositories/ │ ├── interactor/ │ ├── services/ │ ├── validations/ │ ├── endpoints.rb │ └── helpers.rb ├── config/ ├── db/ ├── docs/ ├── lib/ │ ├── schemas/ │ └── values/ ├── public ├── specs ├── config.ru ├── Gemfile ├── Gemfile.lock ├── Rakefile └── README.md 

Cada carpeta en el directorio de apps/ implementa uno u otro subdominio, dentro de cada dominio hay varios patrones: entities , forms , services , etc. Consideraremos cada uno de los patrones aplicados en detalle en uno de los futuros artículos.


Cada uno de estos patrones se implementa en el espacio de nombres correspondiente (espacio de nombres). Por ejemplo, un formulario para crear una solicitud de pago a un servicio de mensajería:


 module Withdrawal #   module Forms #  class Create end end end 

¿Cómo implementar la comunicación entre subdominios?


Veamos un ejemplo específico. Tenemos una cuenta de mensajería: Registrations::Entities::Account . Se refiere al subdominio Registrations , ya que consideramos este dominio no como un proceso de registro, sino como una tabla de cuentas y un libro de registro, como se indica en nuestra documentación comercial.


Tenemos dos procesos en la ejecución de los cuales accedemos a esta cuenta.


  • Crear una cuenta (registro)
  • Creación de una solicitud de retiro de fondos ganados por un servicio de mensajería (Wihtdrawal)

Como vemos, estos dos procesos pertenecen a diferentes subdominios: Registro y Wihtdrawal.


 module Registrations module Serivices class CreateAccount def call account = Entities::Account.new end end end end module Withdrwals module Serivices class CreateOrder def call account = Registrations::Entities::Account.new end end end end 

En el primer caso, se implementará una llamada a la clase a través de una llamada a Entities::Account . Y en el segundo caso, a través de una llamada explícita a Registrations::Entities::Account . Es decir si especificamos explícitamente un subdominio, entonces la clase es de otro subdominio y, por lo tanto, indicamos claramente la conexión.


Si la clase no se aplica explícitamente a ninguno de los subdominios, tiene sentido moverla a la carpeta lib/ . Como regla, estas son clases que implementan el patrón 'ValueObject'. Examinaremos este patrón con más detalle en uno de los siguientes artículos.


Implementación a través del modelo.


Para citar a Eric Evans:


Si la arquitectura del programa, o al menos una parte central del mismo, no corresponde a la estructura del modelo de dominio, dicho modelo es prácticamente inútil, y la operación correcta del programa también debe cuestionarse. Al mismo tiempo, las relaciones demasiado complejas entre modelos y funciones en la arquitectura de software son difíciles de entender, y en la práctica son difíciles de mantener a medida que cambia la arquitectura.

Recordemos el ejemplo de un buen modelo que ya cité al comienzo de este artículo: un mapa topográfico. Nuestro objetivo es poder encontrar rápidamente la distancia entre dos asentamientos. Podríamos usar una tabla de referencia que muestre dos puntos entre ciudades. Y podemos usar la tarjeta. Tanto allí como allí obtenemos el mismo resultado aproximadamente al mismo tiempo. Pero el mapa es más compacto, muestra con mayor precisión el área temática, es más universal. El mapa como modelo es increíblemente expresivo. Y si lo consideramos dentro del marco de esta tarea, medir la distancia es más conveniente en el mapa que en el territorio en sí, lo que refleja. Un modelo que refleja un área temática puede superarlo en algunas propiedades. Realmente es asombroso.


La implementación del modelo es siempre un proceso creativo con resultados impredecibles. La calidad de su código no es su rendimiento, ni su complejidad, es simplicidad y expresividad. Mejore a través de la refactorización constante, hágalo flexible y elimine todo lo innecesario. Separe la capa que será responsable de la lógica empresarial del modelo de las capas cuya necesidad se debe a la implementación técnica. La forma en que logramos hacer esto se describirá más adelante.




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


All Articles