Domain Driven Design (Parte 1: Dominio)

Dado el increíble crecimiento tecnológico de los últimos años, el desarrollo de software se ha visto forzado a evolucionar muy rápido, con ello, nacieron frameworks y librerías que nos permitían crear aplicaciones a velocidad increíble en en todos los ramos posibles, desde aplicaciones móviles con ambientes híbridos hasta librerías de Machine Learning e inteligencia artificial. El desarrollo web no es la excepción, existen múltiples herramientas que te permiten crear aplicaciones desde frontend (cómo react, vue o svelte), backend (como laravel, spring, rails o django) o aplicaciones fullstack en tiempo real (como metheor o express + socket.io).

Todo parece ser muy bueno hasta que llegamos al punto de mantenibilidad, cuando necesitamos crear un producto cuyo ciclo de vida es para mucho años, en este caso, nos veremos afectados por diversas cuestiones: haciendo énfasis en la parte técnica, nos encontrarnos con actualizaciones del framework, dependencias e inclusive actualizaciones del lenguaje mismo, haciendo que el desarrollo del producto se prolongue demasiado y afectado directamente en los costos del desarrollo.

Para solucionar este tipo de problemas existen arquitecturas que intentan separar lo más posible las dependencias del proyecto de los requerimientos del mismo, tal es caso de Domain Driven Design (o DDD).

Ahora bien, DDD se basa en tres principales capas, Dominio, Aplicación e Infraestructura (Puede haber variantes muy pequeñas). En esta serie de posts intentaré explicar en qué consiste DDD comenzando por la capa de dominio.

Dominio

La capa de dominio es en núcleo de DDD, en ella se contienen todas las clases que "modelan" a los componentes del sistema, por ejemplo, la clase Usuario, Producto, Precio, entre otros y también en la capa de dominio se crean aquellos contratos (interfaces) que deben ser seguidos por los nuestras fuentes de datos (repositorios).

Entidades y objetos de valor

Hablado de las clases base, tenemos 2 posibles variantes, Entidades y Objetos de valor:

  • Entidades: Representan un objeto clave de nuestra aplicación, como lo puede ser un Usuario, las entidades tienden a mutar a lo largo del tiempo y tienen un grado alto de persistencia en las bases de datos, la principal característica de las entidades, es que tienden a tener un identificador.
  • Objetos de valor: A diferencia de una entidad, un objeto de valor es inmutable, regularmente se utilizan para crear propiedades de una entidad, por ejemplo, si se tuviera nuevamente la entidad Usuario, cuyo id es único, el id puede ser considerado como un objeto de valor, dado que nunca va a cambiar a lo largo de todo el ciclo de vida del software.

Nota: Es importante mencionar que las clases que representan entidades y objetos de valor, pueden cambiar de postura dependiendo del proyecto y el requerimiento, por ejemplo, en algunos sistemas, las direcciones postales pueden ser una entidad y en otros simplemente una propiedad de una entidad (objeto de valor).

Agregados

Otro concepto muy importante dentro de la capa de dominio son los agregados, un agregado es una referencia a un grupo de objetos de dominio, un ejemplo muy práctico en un e-commerce es una Orden o Pedido, el cual se compone de una serie de Artículos (en dónde un Articulo es una entidad).

El objetivo de un agregado es poder crear transacciones sobre ese grupo en concreto de entidades u objetos de valor, en dónde, regresando al ejemplo del Pedido, podríamos aplicar un descuento sobre el total o sobre ciertos productos si se cumplen ciertas condiciones.

Es importante no confundir un agregado con una colección, los agregados están dirigidos por conceptos clave de dominio, mientras que una colección siempre es genérica.

Repositorios

Un repositorio es un contrato en el cual establecemos aquellos métodos que necesitemos para obtener/persistir los datos de nuestra aplicación.

El objetivo de crear un repositorio dentro de la capa de dominio es mantener el control de todo lo que necesitamos independientemente de la fuente de datos que utilicemos, de esta manera si utilizamos MySQL, Oracle, MongoDB, Firebase o filesystem o incluso todas en conjunto, la aplicación seguirá funcionando, y si en algún futuro necesitamos reemplazar alguna fuente o motor de DB, esto no involucra cambios en la capa de dominio, ni siquiera en la capa de aplicación, solo se creará la nueva implementación en la capa de infraestructura siguiendo contrato y asunto resuelto.

Fábricas (Factories)

Las fábricas son clases que nos permiten crear instancias de nuestras entidades, objetos de valor o agregados basadas en diversos parámetros y condiciones, de este concepto en general existe mucha información, por lo que no haré tanto énfasis.

Evento de dominio

Un evento es una forma de comunicar que alguno de nuestros elementos de dominio ha tenido cierto tipo de interacción, los eventos pueden ser de muchos tipos, desde la creación de una instancia, la persistencia de la misma, la modificación de alguna propiedad, etc. El objetivo de los eventos es notificar a toda la aplicación con el fin de que se propaguen tantas acciones en consecuencia sean necesarias.

Para sacar el máximo provecho de un evento se requiere de escuchadores, estos escuchadores son clases que tienden a realizar acciones adicionales en consecuencia a un evento, por ejemplo, cuando un usuario se crea, se suele enviar un correo de bienvenida; en un ambiente de DDD se ejecutaría el caso de uso de CrearUsuario quien a su vez, lanzará el evento UsuarioCreado.

Por otro lado, tendremos un escuchador EnviarCorreoBienvenida, que se disparará en cuanto el evento UsuarioCreado sea lanzado.

La manera en la cual los escuchadores son notificados, es a través de un bus que está conectado a toda nuestra aplicación, este bus se maneja dentro de la capa de aplicación y se hablará en futuros posts.

Una característica muy importante de los eventos, es que se pueden manejar de manera asíncrona (si así se desea), dejando que la interacción con el usuario se dé de manera muy rápido y creando colas (o queues) que se encarguen de ejecutar dichos eventos en otra ocasión o en paralelo, aumentando los tiempos de respuesta.

Nota: Los eventos, son clases DTO (data transfer objects) u objetos planos que suelen contener información importante del evento en curso, es importante que estas clases utilizan datos primitivos o escalares ya que de ésta manera pueden ser serializados para delegar su ejecución a algún comando con la base de datos o a una cola como rabbitmq

El motivo por el cuál se suelen utilizar eventos en vez de escribir el código como programación lineal, es que en cualquier momento se pueden agregar más eventos y/o eliminarlos si llegase a cambiar el requerimiento, nuevamente, teniendo una afectación mínima en el código de la aplicación.

Conclusión

A pesar de que la capa de dominio es meramente la representación conceptual del sistema y los contratos a seguir, es una de las capas más importantes, ya que define las bases para las siguientes capas. Es importante mencionar que todas aquellas clases que representen algún concepto, como Excepciones personalizadas y DTOs o modelos que no tengan lógica implícita, pueden ser creadas dentro de la capa de dominio.

Recuerda que si tienes alguna duda, sugerencia u observación sobre el contenido, puedes dejarme un comentario, o enviarme un correo a juls0593@gmail.com. Espero que el blog de hoy te haya sido de mucha ayuda.

¡Hasta la próxima!