Introducción a DDD (Domain-Driven Design)

En este artículo, haré una rápida introducción a Domain-Driven Design (DDD), con algun ejemplo para entender los conceptos básicos del diseño guiado por el dominio.

  1. ¿Qué es Domain-Driven Design (DDD)?
  2. Clean architecture y DDD
  3. ¿Cuándo usar DDD?
  4. Pros y contras de DDD
  5. Implementación de software
  6. Procesos de desarrollo
  7. Comunicación
  8. Modelado

¿Qué es Domain-Driven Design (DDD)?

DDD es una metodología que se centra en la creación de un modelo de dominio sofisticado que refleje con precisión el dominio del negocio. Se centra en comprender y modelar el dominio de un problema en lugar de enfocarse únicamente en los aspectos técnicos de la implementación. Es decir, el modelo de dominio se convierte en el corazón del software y sirve como una guía para la implementación. Al enfocarse en el lenguaje y los conceptos del dominio, se puede mejorar la comunicación entre los expertos en el dominio (usualmente los usuarios o propietarios del negocio) y los desarrolladores de software, lo que puede ayudar a reducir la brecha entre la comprensión de los requisitos del negocio y la implementación de los mismos en el software.

Clean architecture y DDD

Clean Architecture aborda las capas arquitectónicas y las gestión de dependencias, mientras que DDD proporciona principios y patrones para el modelado de dominios y la colaboración con expertos en el dominio. Ambas se complementan en el diseño y desarrollo de software.

¿Cuándo usar DDD?

DDD, es una aproximación al desarrollo de software que se enfoca en entender el negocio y su dominio, y reflejarlo en el diseño del software. Esta aproximación puede ser utilizada en diferentes contextos y proyectos, pero aquí hay algunos casos donde se recomienda su uso:

  1. Proyectos complejos: DDD es especialmente útil en proyectos que involucran un alto grado de complejidad. En estos casos, la comprensión profunda del dominio del negocio puede ayudar a los desarrolladores a entender mejor los requisitos y necesidades del sistema y, por lo tanto, a construir un software más efectivo.
  2. Equipos grandes: DDD puede ayudar a alinear a los desarrolladores y garantizar que todos tengan una comprensión común del negocio y sus requerimientos. Además, puede ayudar a dividir el sistema en diferentes módulos o componentes que puedan ser desarrollados de manera independiente, lo que puede facilitar el trabajo del equipo.
  3. Sistemas altamente interactivos: Cuando el software debe interactuar con otros sistemas, DDD puede ayudar a garantizar que las interacciones sean efectivas y precisas.
  4. Proyectos de larga duración: DDD puede ayudar a mantener el enfoque en el dominio del negocio y evitar la degradación de la calidad del software debido a cambios constantes en los requisitos o la tecnología.

DDD es una aproximación útil en cualquier proyecto de desarrollo de software donde la comprensión del negocio es crítica y los requerimientos del sistema son complejos. También puede ser particularmente útil en proyectos grandes y de larga duración, donde es necesario garantizar la alineación del equipo y la efectividad de las interacciones con otros sistemas.

Pros y contras de DDD

Pros

  • Debido a que DDD nos guía para centrarnos en partes pequeñas, individuales y casi autónomas de nuestro dominio, el proceso y el software resulta más flexible. Foco en el desarrollo de un área dividida del dominio (subdominio) a través de Bounded Context’s.
  • DDD brinda un camino claro y manejable a través de un problema muy complejo.
  • Comunicación efectiva entre expertos del dominio y expertos técnicos a través de Ubiquitous Language.
  • El software es más cercano al dominio, y por lo tanto es más cercano al cliente.
  • Código bien organizado, permitiendo las pruebas de las distintas partes del dominio de manera aisladas.
  • Lógica de negocio reside en un solo lugar, y dividida por contextos.
  • Patrones y prácticas beneficiarias para la aplicación.
  • Mantenibilidad a largo plazo.

Contras

  • Aislar la lógica de negocio con un experto de dominio y el equipo de desarrollo suele llevar mucho esfuerzo a nivel tiempo.
  • Necesitamos un experto de dominio (es quien realmente conoce la realidad y es quién nos puede ayudar a validar los modelos que vamos creando. El experto en el dominio desempeña un rol fundamental en el desarrollo de la aplicación).
  • Una curva de aprendizaje alta, con patrones, procedimientos,…
  • Este enfoque sólo es sugerido para aplicaciones donde el dominio sea complejo, no es recomendado para simples CRUD’s.

Implementación software

  1. Entities
  2. Value Objects
  3. Aggregates
  4. Factories
  5. Repositories
  6. Domain Events
  7. Services
  8. Layered Architecture

En DDD, la implementación de software se basa en la creación de un modelo de dominio rico y expresivo, que se divide en diferentes componentes clave, como: EntitiesValueObjectsRepositoriesFactories, etc.

1. Entities

Las entidades son objetos que tienen una identidad única y propiedades que cambian con el tiempo. Las entidades representan elementos del mundo real que son importantes para el negocio y que deben ser persistidos en la base de datos.

Son los componentes básicos del modelado del dominio y son el primer lugar donde debemos pensar para comenzar a poner la lógica del dominio.

Usar int o GUID en las entidades

GUID Pros:

  • Único en cada tabla, cada base de datos, cada servidor.
  • Permite combinar fácilmente registros de diferentes bases de datos.
  • Permite una fácil distribución de bases de datos a través de múltiples servidores.
  • Puede generar IDs en cualquier lugar, en lugar de tener que ir de ida y vuelta a la base de datos.
  • La mayoría de los escenarios de replicación requieren columnas GUID de todos modos.

GUID Cons:

  • Es 4 veces más grande que el valor de índice tradicional de 4 bytes; esto puede tener serias implicaciones de rendimiento y almacenamiento si no tiene cuidado.
  • Engorroso de depurar donde userid='{BAE7DF4-DDF-3RG-5TY3E3RF456AS10}’
  • Los GUID generados deben ser parcialmente secuenciales para un mejor rendimiento y para permitir el uso de índices agrupados.

Conclusión, si está seguro del rendimiento y no planea replicar o fusionar registros, use int y configúralo como incremento automático.

2. Value Objects

Los objetos valor son objetos que no tienen una identidad única, sino que se definen por su estado. Los objetos de valor representan conceptos importantes para el negocio, como fechas, direcciones o cantidades, y son inmutables.

Los value object representan elementos del modelo que se describen por el QUÉ son, y no por QUIÉN o CUÁL son.

3. Aggregates

Los agregados son grupos cohesivos de entidades y objetos de valor que se tratan como una unidad coherente en el modelo de dominio. Los agregados se definen por las reglas de negocio y se utilizan para garantizar la consistencia de los datos y la transaccionalidad.

Un agregado tendrá uno de sus objetos componentes como la raíz del agregado. Cualquier referencia desde fuera del agregado solo debe ir a la raíz del agregado. La raíz puede así asegurar la integridad del agregado como un todo. Agrupar las entidades y objetos de valor en agregados y definir límites alrededor de cada uno. Elija una entidad para que sea la raíz de cada agregado y permita que los objetos externos contengan referencias solo a la raíz.

4. Factories

Las factorías son objetos responsables de la creación de nuevas entidades y objetos de valor. Las factorías permiten la encapsulación de la lógica de creación de objetos y facilitan la creación de objetos complejos.

Utiliza un Factory para crear objetos complejos y agregados, asegurándose de que el cliente no tenga conocimiento de los detalles internos y la funcionalidad de la manipulación de ese objeto.

Cuando la creación de un agregado completo, internamente consistente, o un objeto de gran valor, se vuelve complicado o revela demasiado de la estructura interna, las fábricas brindan encapsulamiento.

También aseguran la estandarización de la creación de instancias de objetos y aseguran que los objetos no se preocupen por la creación en sí mismos. Esto ayuda a mantener limpio el dominio y garantiza que se mantengan los límites.

5. Repositories

Los repositorios son objetos que permiten la persistencia de las entidades y objetos de valor en la base de datos. Los repositorios actúan como una abstracción sobre la capa de acceso a datos y permiten la separación de las responsabilidades entre el modelo de dominio y la infraestructura.

Un repositorio es un servicio que utiliza una interfaz global para brindar acceso a todas las entidades y objetos de valor que se encuentran dentro de una colección de agregados.

6. Domain Events

Los eventos de dominio son eventos que se disparan en respuesta a acciones específicas en el modelo de dominio. Los eventos de dominio permiten la separación de las responsabilidades y la comunicación entre diferentes componentes del sistema.

7. Services

Los servicios son objetos que encapsulan lógica de negocio compleja y que no pertenecen a ninguna entidad o agregado en particular. Los servicios se utilizan para realizar operaciones complejas que no se pueden realizar directamente en una entidad o agregado.

Cuando un proceso significativo o una transformación en el dominio no es una responsabilidad natural de una entidad u objeto de valor, agregue una operación al modelo como una interfaz independiente declarada como un servicio.

8. Layered Architecture

Arquitectura en capas, es una arquitectura que separa los diferentes componentes del sistema en capas lógicas y físicas. La arquitectura en capas permite la separación de responsabilidades y la escalabilidad del sistema.

No usar una arquitectura en capas haría que el código fuera difícil de leer, mezclará contextos limitados y también hará que el código no se pueda probar de forma aislada.

EJEMPLO

Aquí te muestro cómo se podrían aplicar los componentes de DDD en este contexto:

Aggregate Pattern
  1. Entities: tenemos una entidad comprador llamada «Buyer» que tiene una identidad única (ID de comprador) y propiedades como el nombre completo, la dirección de correo electrónico, etc. También tenemos una entidad pedido llamada «Order» que tiene una identidad única (ID de pedido), propiedades como los datos de pedidos «OrderData», la dirección «Address», y una colección de artículos de pedido «OrderItems».
  2. Value Objects: tenemos un objeto de valor llamado «Address» que representa la dirección de envío de un pedido. Este objeto de valor podría tener propiedades como la calle, la ciudad, etc.
  3. Aggregates: tenemos dos agregados, uno llamado «Buyer» que contiene una sola entidad, por lo tanto es la raíz del agregado. El otro agregado es «Order» que tiene múltiples entidades y un objeto de valor. La entidad «Order» sería la raíz del agregado, toda referencia desde fuera del agregado solo debe ir a la raíz del agregado, sería responsable de garantizar la consistencia de los datos y la transaccionalidad al crear, actualizar o cancelar un pedido.
  4. Factories: tenemos una factoría llamada «OrderFactory» que se encarga de crear nuevos pedidos. Esta factoría incluye lógica compleja para la creación de nuevos pedidos, como la validación de campos y la generación de IDs únicos.
  5. Repositories: tenemos un repositorio llamado «OrderRepository» que se encarga de la persistencia de los pedidos en la base de datos. El repositorio actúa como una abstracción sobre la capa de acceso a datos y permite la separación de las responsabilidades entre el modelo de dominio y la infraestructura.
  6. Domain Events: tenemos un evento de dominio llamado «OrderCreated» que se dispara cuando se crea un nuevo pedido. Este evento se utiliza para enviar un correo electrónico de confirmación al comprador y actualizar otros componentes del sistema.
  7. Services: tenemos un servicio llamado «OrderService» que actúa como un punto de entrada para las operaciones relacionadas con los pedidos. Este servicio utiliza el repositorio y la factoría mencionados anteriormente para realizar operaciones de creación, actualización o cancelación de pedidos.
  8. Layered Architecture: podemos organizar nuestra aplicación en capas lógicas, como una capa de presentación, una capa de aplicación, una capa de dominio y una capa de infraestructura. Cada capa tendría su propia responsabilidad y utilizaría los conceptos mencionados anteriormente para cumplir con esa responsabilidad.

Procesos de desarrollo

Los procesos de desarrollo en DDD se enfocan en la colaboración entre los expertos en el dominio y los desarrolladores de software. Los expertos en el dominio proporcionan información sobre los requisitos del negocio y los desarrolladores utilizan esta información para crear un modelo de dominio rico y expresivo. Este proceso de colaboración continua durante todo el ciclo de vida del software, lo que permite una mayor comprensión y adaptación a los cambios y necesidades del negocio.

  1. Continuous Integration
  2. Big Ball of Mud
  3. Anticorruption Layer
  4. Separate Ways

1. Continuous Integration

Una práctica de desarrollo de software en la que los desarrolladores fusionan con frecuencia sus cambios de código en un repositorio central, lo que permite la detección temprana de conflictos y errores. Esto ayuda a garantizar que el código esté siempre en funcionamiento y reduce el tiempo y el esfuerzo necesarios para integrar los cambios más adelante.

2. Big Ball of Mud

Este término se refiere a un sistema de software que se ha vuelto complejo y difícil de mantener debido a la falta de estructura y diseño arquitectónico. El término implica que el sistema es un lío enredado, sin límites claros ni separación de intereses.

3. Anticorruption Layer

Un patrón de diseño utilizado en el desarrollo de software para evitar la contaminación de un sistema nuevo por un sistema antiguo o heredado. La capa anticorrupción actúa como un puente entre los dos sistemas, traduciendo los datos y las interacciones entre ellos para garantizar que el nuevo sistema permanezca limpio y sin contaminación.

4. Separate Ways

Esto se refiere a la idea de que las diferentes partes de un sistema de software deben desarrollarse independientemente unas de otras, con dependencias mínimas entre ellas. Esto permite una mayor flexibilidad y escalabilidad, así como un mantenimiento y una depuración más sencilla.

Comunicación

La comunicación es fundamental para el éxito del proyecto. Los desarrolladores y los expertos en el dominio deben utilizar un lenguaje común para garantizar que la comprensión de los requisitos del negocio sea clara y precisa. Además, la comunicación debe ser continua y fluida para permitir la adaptación a los cambios y la resolución de problemas de manera oportuna.

  1. Ubiquitous Language

1. Ubiquitous Language

Uno de los mayores problemas que surgen durante el desarrollo de proyectos software es la comunicación entre los desarrolladores y los expertos del dominio.

La idea de crear un lenguaje ubicuo que nos ayude a modelar la realidad y, lo que es más importante, a dialogar sobre ella de una forma precisa, es muy útil, aunque sólo sea para la comunicación interna del equipo. Además esto nos ayuda a ponernos en la piel del usuario, intentar entender cómo piensa, a qué problemas se enfrenta y cómo resolverlos. De esta forma podremos resolver lo que realmente preocupa al usuario y crearemos un modelo pensado para resolver esos problemas y no otros.

Para ser capaces de generar un lenguaje común, los desarrolladores tenemos que obtener el conocimiento necesario del dominio, mientras que los expertos del dominio deben formar parte activa en el desarrollo del software. De manera que tanto el dominio del problema, como los elementos del diseño del software (clases, relaciones, etc…) formen parte del lenguaje común y sean entendidas por los desarrolladores y los expertos del dominio.

Modelado

El modelado es un examen intenso del problema, la clave de esto es trabajar con los expertos del dominio para identificar el Core Domain o dominio principal y otros Subdomains o subdominios. Otro aspecto fundamental del modelado es identificar los Bounded Context o contexto delimitado, dentro de cada uno de estos contextos delimitados modelamos un subdominio en particular.

Como resultado de modelar un contexto delimitado se identificaran entidades, objetos valor, aggregates, etc…

La delimitación de bounded contexts, subdominios y el core domain ayuda a enfocar el diseño del software y a garantizar que el equipo de desarrollo tenga una comprensión común de los requisitos del negocio. El context map es una herramienta útil para visualizar la relación entre los diferentes bounded contexts y subdominios y para garantizar que el sistema en su conjunto sea coherente.

  1. Dominio y Subdominios
  2. Bounded Context
  3. Context Map

1. Dominio y Subdominios

El dominio es el área del negocio que se está modelando en el software. El dominio puede dividirse en subdominios, que son áreas más específicas dentro del dominio principal (Core Domain). La delimitación de los subdominios puede ser útil para enfocarse en áreas específicas del negocio y para evitar que el modelo de dominio se vuelva demasiado grande y complejo.

2. Bounded Context

Un bounded context es una delimitación lógica del modelo de dominio que establece límites para un conjunto de conceptos y su lógica asociada. Es una forma de dividir un modelo de dominio complejo en piezas más manejables y coherentes. Dentro de cada bounded context, los términos y conceptos tienen un significado específico y pueden diferir de otros bounded context.

Los límites se pueden definir en términos de organización del equipo, uso dentro de partes específicas de la aplicación e incluso bases de código y esquemas de bases de datos.

3. Context Map

El context map es una herramienta visual que se utiliza para ilustrar la relación entre los diferentes bounded contexts y subdominios. Es una forma de mostrar cómo los diferentes componentes del sistema interactúan entre sí y cómo se comunican los diferentes equipos de desarrollo. El context map puede ser utilizado para identificar conflictos entre los diferentes bounded contexts y para asegurar una integración efectiva del sistema en su conjunto.

Empezar a trabajar con DDD

Para empezar a trabajar con DDD, aquí hay algunos pasos generales que puedes seguir:

  1. Aprender los conceptos básicos: antes de comenzar a aplicar DDD a tus proyectos, es importante que entiendas los conceptos y principios fundamentales. Lee libros, artículos y recursos en línea que describan los conceptos y las mejores prácticas de DDD. Algunos recursos útiles incluyen «Domain-Driven Design» de Eric Evans y «Implementing Domain-Driven Design» de Vaughn Vernon.
  2. Identificar el dominio principal: identifica cuál es el núcleo del negocio o del problema que estás tratando de resolver. Este es el dominio central que deseas modelar utilizando DDD. Puedes empezar por hacer un análisis de los procesos de negocio, los requisitos del usuario y los problemas a resolver.
  3. Identificar bounded contexts: una vez que tengas una idea clara del core domain, identifica los bounded contexts que lo componen. Cada bounded context es una delimitación lógica del modelo de dominio y debe estar claramente definido. Para identificar los bounded contexts implica identificar las necesidades del negocio, los dominios específicos, los límites delimitados, definir las interfaces y establecer equipos responsables.
  4. Crear un context map: crea un mapa de contexto que muestre cómo se relacionan los diferentes bounded contexts y cómo se comunican entre sí. Esto es importante para garantizar una comunicación clara entre los equipos de desarrollo y para evitar problemas de integración en el futuro.
  5. Implementar un modelo de dominio: una vez que hayas identificado los bounded contexts y tengas un context map claro, comienza a implementar un modelo de dominio que refleje las necesidades y requisitos del negocio. Utiliza patrones y prácticas de DDD para garantizar que el modelo sea coherente, mantenible y escalable.
  6. Refinar y mejorar continuamente: como con cualquier práctica de desarrollo de software, es importante refinar y mejorar continuamente el modelo de dominio. Realiza pruebas y validaciones constantes, trabaja con los usuarios finales para entender sus necesidades y haz ajustes según sea necesario.

Eso es todo, por el momento, seguiré actualizando y agregando contenido en este post. Espero que te haya resultado interesante✌️