Compartir a través de


Diseño de un microservicio orientado a DDD

Sugerencia

Este contenido es un extracto del libro electrónico, ".NET Microservices Architecture for Containerized .NET Applications" (Arquitectura de microservicios de .NET para aplicaciones de .NET contenedorizadas), disponible en Documentación de .NET o como un PDF descargable y gratuito que se puede leer sin conexión.

Miniatura de la portada del libro electrónico 'Arquitectura de microservicios de .NET para aplicaciones .NET contenedorizadas'.

El diseño dirigido por el dominio (DDD) defiende el modelado basado en la realidad empresarial que es relevante para tus casos de uso. En el contexto de la creación de aplicaciones, DDD habla sobre problemas como dominios. Describe áreas de problemas independientes como Contextos delimitados (cada Contexto delimitado se correlaciona con un microservicio) y enfatiza un lenguaje común para hablar sobre estos problemas. También sugiere muchos conceptos y patrones técnicos, como entidades de dominio con modelos enriquecidos (sin modelo de dominio anémico), objetos de valor, agregados y reglas de raíz agregada (o entidad raíz) para admitir la implementación interna. En esta sección se presenta el diseño y la implementación de esos patrones internos.

A veces, estas reglas y patrones técnicos de DDD se perciben como obstáculos que tienen una curva de aprendizaje empinada para implementar enfoques DDD. Pero la parte importante no es los propios patrones, sino la organización del código para que esté alineado con los problemas empresariales y el uso de los mismos términos empresariales (lenguaje omnipresente). Además, los enfoques DDD solo se deben aplicar si va a implementar microservicios complejos con reglas de negocio significativas. Las responsabilidades más sencillas, como un servicio CRUD, se pueden administrar con enfoques más sencillos.

Dónde dibujar los límites es la tarea clave al diseñar y definir un microservicio. Los patrones DDD le ayudan a comprender la complejidad del dominio. Para el modelo de dominio para cada contexto enlazado, se identifican y definen las entidades, los objetos de valor y los agregados que modelan el dominio. Se compila y se refina un modelo de dominio que se encuentra dentro de un límite que define el contexto. Y eso es explícito en forma de microservicio. Los componentes dentro de esos límites terminan siendo sus microservicios, aunque en algunos casos un microservicio bc o empresarial puede estar compuesto de varios servicios físicos. DDD trata sobre límites, al igual que los microservicios.

Mantener los límites de contexto de microservicio relativamente pequeños

Determinar dónde colocar límites entre contextos delimitados busca un equilibrio entre dos objetivos en competencia. En primer lugar, desea crear inicialmente los microservicios más pequeños posibles, aunque eso no debe ser el controlador principal; debe crear un límite en torno a las cosas que necesitan cohesión. En segundo lugar, le interesa evitar comunicaciones locuaces entre microservicios. Estos objetivos pueden contradecirse entre sí. Para equilibrar los microservicios, descompón el sistema en tantos pequeños microservicios como puedas hasta que veas que los límites de comunicación crecen rápidamente con cada intento adicional de separar un nuevo Contexto Delimitado. La cohesión es clave dentro de un único contexto limitado.

Se parece a una inadecuada intuición de código de cercanía al implementar las clases. Si dos microservicios necesitan colaborar mucho entre sí, probablemente deberían ser el mismo microservicio.

Otra manera de mirar este aspecto es la autonomía. Si un microservicio debe confiar en otro servicio para atender directamente una solicitud, no es realmente autónomo.

Niveles en microservicios de DDD

La mayoría de las aplicaciones empresariales con una complejidad técnica y empresarial significativa se definen mediante varias capas. Las capas son un artefacto lógico y no están relacionados con la implementación del servicio. Existen para ayudar a los desarrolladores a administrar la complejidad en el código. Es posible que diferentes capas (como la capa de modelo de dominio frente a la capa de presentación, etc.) tengan tipos diferentes, que exigen traducciones entre esos tipos.

Por ejemplo, una entidad podría cargarse desde la base de datos. A continuación, parte de esa información o una agregación de información, incluidos datos adicionales de otras entidades, se puede enviar a la interfaz de usuario del cliente a través de una API web REST. El punto aquí es que la entidad de dominio está contenida en la capa de modelo de dominio y no debe propagarse a otras áreas a las que no pertenece, como a la capa de presentación.

Además, debe tener entidades siempre válidas (consulte la sección Diseño de validaciones en la capa de modelo de dominio) controlada por raíces agregadas (entidades raíz). Por lo tanto, las entidades no deben estar ligadas a las vistas del cliente, ya que, en el nivel de la interfaz de usuario, puede que algunos datos aún no hayan sido validados. Este es precisamente el propósito del ViewModel. ViewModel es un modelo de datos exclusivamente para las necesidades de la capa de presentación. Las entidades de dominio no pertenecen directamente a ViewModel. En cambio, debe traducir entre ViewModels y entidades de dominio, y viceversa.

Al abordar la complejidad, es importante tener un modelo de dominio controlado por raíces agregadas que asegúrese de que todas las invariables y reglas relacionadas con ese grupo de entidades (agregado) se realizan a través de un único punto de entrada o puerta, la raíz de agregado.

En la figura 7-5 se muestra cómo se implementa un diseño en capas en la aplicación eShopOnContainers.

Diagrama que muestra las capas de un microservicio de diseño controlado por dominio.

Figura 7-5. Niveles de DDD en el microservicio de ordenación en eShopOnContainers

Las tres capas de un microservicio DDD, como Ordering. Cada capa es un proyecto de VS: la capa de aplicación es Ordering.API, la capa de dominio es Ordering.Domain y la capa de infraestructura es Ordering.Infrastructure. Quiere diseñar el sistema para que cada capa se comunique solo con ciertas otras capas. Ese enfoque puede ser más fácil de aplicar si las capas se implementan como bibliotecas de clases diferentes, ya que puede identificar claramente qué dependencias se establecen entre bibliotecas. Por ejemplo, la capa del modelo de dominio no debe depender de ninguna otra capa (las clases del modelo de dominio deberían ser objetos de clases comunes, o POCO). Como se muestra en la figura 7-6, la biblioteca de capas Ordering.Domain solo tiene dependencias en las bibliotecas de .NET o paquetes NuGet, pero no en ninguna otra biblioteca personalizada, como la biblioteca de datos o la biblioteca de persistencia.

Captura de pantalla de las dependencias Ordering.Domain.

Figura 7-6. Las capas implementadas como bibliotecas permiten un mejor control de las dependencias entre capas

La capa del modelo de dominio

El excelente libro de Eric Evans Domain Driven Design dice lo siguiente sobre la capa de modelo de dominio y la capa de aplicación.

Nivel de modelo de dominio: responsable de representar conceptos del negocio, información sobre la situación del negocio y reglas de negocios. El estado que refleja la situación del negocio se controla y se usa aquí, aunque los detalles técnicos del almacenamiento se delegan a la infraestructura. Esta capa es el núcleo del software empresarial.

La capa del modelo de dominio es donde se expresa la empresa. Al implementar una capa de modelo de dominio de microservicio en .NET, esa capa se codifica como una biblioteca de clases con las entidades de dominio que capturan datos más comportamiento (métodos con lógica).

Siguiendo los principios de Ignorancia de la Persistencia y Ignorancia de la Infraestructura, esta capa debe ignorar por completo los detalles sobre la persistencia de datos. Estas tareas de persistencia deben realizarse en el nivel de infraestructura. Por lo tanto, esta capa no debe tomar dependencias directas en la infraestructura, lo que significa que una regla importante es que las clases de entidad del modelo de dominio deben ser POCOs.

Las entidades de dominio no deben tener ninguna dependencia directa (como derivar de una clase base) en ningún marco de infraestructura de acceso a datos, como Entity Framework o NHibernate. Idealmente, las entidades de dominio no deberían derivar ni implementar cualquier tipo definido en ningún framework de infraestructura.

La mayoría de los marcos ORM modernos, como Entity Framework Core, permiten este enfoque, de modo que las clases de modelo de dominio no estén acopladas a la infraestructura. Sin embargo, no siempre es posible tener entidades POCO al usar determinadas bases de datos y marcos NoSQL, como Actors y Reliable Collections en Azure Service Fabric.

Incluso cuando es importante seguir el principio de "Ignorancia de la Persistencia" para su modelo de dominio, no debe pasar por alto las cuestiones relacionadas con la persistencia. Sigue siendo importante comprender el modelo de datos físicos y cómo se asigna a un modelo de objetos entidad. De lo contrario, puede crear diseños imposibles.

Además, este aspecto no significa que pueda tomar un modelo diseñado para una base de datos relacional y moverlo directamente a una base de datos NoSQL o orientada a documentos. En algunos modelos de entidad, el modelo podría ajustarse, pero normalmente no lo hace. Todavía hay restricciones que el modelo de entidad debe cumplir, en función de la tecnología de almacenamiento y la tecnología ORM.

La capa de aplicación

Al pasar a la capa de aplicación, podemos citar de nuevo el libro de Eric Evans Domain Driven Design:

Nivel de aplicación: define los trabajos que se supone que el software debe hacer y dirige los objetos de dominio expresivo para que resuelvan problemas. Las tareas de esta capa son significativas para la empresa o necesarias para la interacción con las capas de aplicación de otros sistemas. Esta capa se mantiene delgada. No contiene reglas de negocio ni conocimientos, sino que solo coordina tareas y delega trabajo a colaboraciones de objetos de dominio en la capa siguiente. No tiene estado que refleje la situación empresarial, pero puede tener un estado que refleje el progreso de una tarea para el usuario o el programa.

La capa de aplicación de un microservicio en .NET se codifica normalmente como un proyecto de API web de ASP.NET Core. El proyecto implementa la interacción del microservicio, el acceso a la red remota y las API web externas que se usan desde la interfaz de usuario o las aplicaciones cliente. Incluye consultas si se usa un enfoque de CQRS, comandos aceptados por el microservicio e incluso la comunicación controlada por eventos entre microservicios (eventos de integración). La API web de ASP.NET Core que representa el nivel de aplicación no debe contener reglas de negocio ni conocimientos de dominio (especialmente reglas de dominio para transacciones o actualizaciones); estos deben ser propiedad de la biblioteca de clases de modelo de dominio. La capa de aplicación solo debe coordinar las tareas y no debe contener ni definir ningún estado de dominio (modelo de dominio). Delega la ejecución de reglas de negocio a las clases del modelo de dominio (raíces agregadas y entidades de dominio), que en última instancia actualizarán los datos dentro de esas entidades de dominio.

Básicamente, la lógica de la aplicación es donde se implementan todos los casos de uso que dependen de un front-end determinado. Por ejemplo, la implementación relacionada con un servicio de API web.

El objetivo es que la lógica de dominio en el nivel de modelo de dominio, sus invariables, el modelo de datos y las reglas de negocio relacionadas deben ser completamente independientes de las capas de presentación y aplicación. La mayor parte de todo, la capa de modelo de dominio no debe depender directamente de ningún marco de infraestructura.

El nivel de infraestructura

La capa de infraestructura es cómo los datos que se mantienen inicialmente en entidades de dominio (en memoria) se conservan en bases de datos u otro almacén persistente. Un ejemplo consiste en usar código de Entity Framework Core para implementar las clases de patrón Repository que usan dbContext para conservar los datos en una base de datos relacional.

De acuerdo con los principios de ignorancia de persistencia e ignorancia de infraestructura mencionados anteriormente, la capa de infraestructura no debe "contaminar" la capa del modelo de dominio. Debe mantener las clases de entidad del modelo de dominio independientes de la infraestructura que se usa para conservar los datos (EF o cualquier otro framework) al no adquirir dependencias fuertes con los frameworks. La biblioteca de clases de la capa del modelo de dominio debe contener únicamente código de dominio, únicamente clases de entidad POCO que constituyen el núcleo del software y totalmente desacopladas de las tecnologías de infraestructura.

Por lo tanto, las capas o bibliotecas de clases y los proyectos deben depender en última instancia de la capa del modelo de dominio (biblioteca), no viceversa, como se muestra en la figura 7-7.

Diagrama que muestra las dependencias que existen entre las capas de servicio de DDD.

Figura 7-7. Dependencias entre capas en DDD

Las dependencias de un servicio DDD, la capa de aplicación depende del dominio y la infraestructura, y la infraestructura depende del dominio, pero el dominio no depende de ninguna capa. Este diseño de capa debe ser independiente para cada microservicio. Como se indicó anteriormente, puede implementar los microservicios más complejos siguiendo patrones DDD, al tiempo que implementa microservicios controlados por datos más sencillos (CRUD simple en una sola capa) de una manera más sencilla.

Recursos adicionales