¡Bienvenidos al modelado enriquecido del dominio!

Rubén Salado
Genially Tech
Published in
9 min readJun 6, 2023

--

Este es el último artículo de la serie “Del modelado anémico al modelado enriquecido del dominio”.

El cambio de una API tipo CRUD a una API basada en Domain-Driven Design va mucho más allá de un mero cambio de código, de establecer una estructura de directorios en particular o de utilizar ciertos tipos de arquitectura. Hay algo mucho más importante y complejo que tiene que ver con la manera de trabajar: la forma en la que pensamos y razonamos acerca del software.

Esto no siempre es algo obvio ni se habla tanto de ello, así que en este post nos vamos a centrar en aspectos quizás menos conocidos que hemos experimentado a la hora de aplicar Domain-Driven Design. En Genially llevamos varios años utilizando este tipo de filosofía de desarrollo y nos ha dado tiempo a vivir todo tipo de situaciones que nos han ayudado a aprender y a mejorar (por las buenas y por las malas).

Un cambio de mentalidad

Casi siempre que se habla de Domain-Driven Design se limita, erróneamente, a los patrones tácticos. Pero también es cierto que es la manera más sencilla de comenzar a adentrarse en “este mundo” y la que tiene un impacto inmediato en la calidad del código.

Estos patrones son en gran parte una colección de buenas prácticas ampliamente conocidas. Nos permiten diseñar e implementar un buen modelo del dominio que cumpla con las reglas de negocio de manera consistente, mantenible y escalable.

A veces parece que por arte de magia y por el simple hecho de utilizar value objects, agregados o servicios de dominio, ya nos convertimos en super-developers y nuestro código es el mejor del mundo mundial. No os vamos a mentir, es algo que también nos ocurrió. Pero la realidad es otra, y mucho más interesante, que tiene que ver con un cambio de mentalidad al aplicar estos patrones para crear un modelo de dominio enriquecido.

El cambio principal que se produce en el equipo es la necesidad de entender muy bien el dominio en el que trabaja. Poner en el centro del desarrollo el dominio frente a los aspectos relacionados con la tecnología que lo resolverá lo cambia todo.

A veces quienes desarrollamos solemos tener cierta tendencia a comenzar por decidir los aspectos técnicos primero y a escribir el código sin llegar a comprender del todo qué (ni para qué) es lo que vamos a construir. Es decir, acabamos dando más importancia al cómo que al qué.

Cuando trabajas sobre un dominio complejo, la filosofía de Domain-Driven Design comienza a brillar. Es aquí cuando, poco a poco, las conversaciones iniciales pasan de decidir qué bibliotecas, recursos, patrones o esquemas vamos a utilizar para resolver el problema, a priorizar el entendimiento del problema al que nos enfrentamos, a comprender todos los conceptos involucrados y sus límites, y cómo se relacionan unos con otros.

Ahora sí, la tecnología queda al servicio del negocio, y no al revés. En nuestro caso este cambio de mentalidad nos ha llevado a alcanzar un nivel de madurez que nos hace querer estar más cerca del problema, del dominio y, en definitiva, que consigamos tener un impacto mayor en el negocio.

El modelo del dominio y el modelo de datos

Una de las primeras consecuencias de poner en el centro nuestro dominio es que en nuestro día a día comenzamos a trabajar con dos modelos distintos, que tienen propósitos diferentes: el modelo del dominio y el modelo de datos.

El modelo del dominio debe procurar que todas las reglas de nuestro negocio se cumplan de manera consistente, así como capturar todos los conceptos, atributos, y cómo se comportan y relacionan unos con otros. Este modelo debe ser altamente tolerante al cambio para poder adaptarse a los cambios de requisitos que sufre nuestro negocio. Idealmente nuestro negocio y nuestro modelo de dominio deberían ser un espejo.

Por otro lado, el modelo de datos debe priorizar que la información pueda ser almacenada y recuperada de la mejor manera posible. Los requisitos de este modelo están relacionados con los propios de la base de datos que se usa, es decir, están relacionados con la durabilidad de los datos, la atomicidad de las transacciones o la optimización de las consultas y escrituras.

Idealmente debería existir una relación 1:1 entre el modelo del dominio y el modelo de datos, pero la realidad no es esa. Por su naturaleza, el modelo de datos no es tan altamente tolerante a los cambios como el modelo del dominio ya que está fuertemente influenciado por la propia tecnología de la base de datos.

Ser conscientes de que trabajamos con estos dos modelos nos ha permitido separar las responsabilidad de ambos. Nuestro modelo del dominio es la fuente de la verdad ya que refleja la realidad de nuestro negocio. Es por ello que nuestro esfuerzo siempre gira en torno a la mejora continua del modelado para reflejar correctamente todas las reglas de nuestro negocio y los límites de los distintos elementos involucrados. Cuando necesitamos optimizar el rendimiento de la lectura y escritura de la información, nos centramos en el modelo de datos.

Casos de uso y testing

El modelo del dominio contiene todos los elementos que están involucrados en los casos de uso de nuestro negocio. Los casos de uso no son más que la manera en que utilizamos nuestro dominio para llevar a cabo aquellas acciones que necesitamos en nuestro negocio. Por ejemplo, publicar un genially, añadir un colaborador o renombrar una carpeta.

Una de las grandes ventajas de adoptar una aproximación enfocada en el dominio es que automáticamente los casos de uso quedan muy explícitos en el código gracias a los denominados application services. De esta manera se hace muy sencillo saber de un simple vistazo, a modo de documentación, cuáles son los casos de uso con los que contamos y cuáles no.

Además, ya que los application services son “la puerta de entrada” a nuestro dominio, se convierten en los candidatos ideales a la hora de realizar una buena estrategia de pruebas. Tener una alta cobertura de casos de uso nos asegura que nuestro dominio, de manera transitiva, está siendo también bien cubierto por pruebas que validan las reglas de negocio.

Al enfocar la estrategia de pruebas de los casos de uso como pruebas de caja negra nos hace crear pruebas que son “poco frágiles”. Por poco frágiles nos referimos a que si el modelo del dominio cambia por una mejora en el propio modelo y no por la lógica de negocio en sí, nuestras pruebas seguirán funcionando correctamente.

Por ejemplo, nuestra suite de test nunca deberían fallar por la creación de nuevos value objects, por la refactorización de un agregado o por mover lógica del application service al agregado. Esto nos da la tranquilidad de iterar y refactorizar sin miedo a cambiar ninguna regla de negocio por error.

Eventos de dominio y comunicación basada en eventos

Los eventos de dominio son una de las piezas que conforman nuestro modelo enriquecido del dominio (aunque realmente este concepto no forma parte del libro azul de Domain-Driven Design y se introdujo por primera vez posteriormente en el libro rojo). El objetivo de estos eventos es informar a otras partes del dominio de que algo de interés ha ocurrido.

Esta manera de trabajar donde un application service realiza una mutación sobre un agregado y se emite un evento de dominio es ideal a la hora de implementar side-effects de manera desacoplada y flexible. Por ejemplo, cada vez que un usuario publica un genially se emite el evento GeniallyPublished para que otros agregados de nuestro contexto reaccionen de alguna manera, ya sea enviando una notificación, incrementando un contador o cualquier otra acción derivada.

Además, al trabajar con eventos de dominio se puede sacar provecho de ciertas ventajas como la comunicación asíncrona entre aplicaciones mediante el envío y recepción de eventos o la posibilidad de auditar todo lo que ocurre en nuestro dominio de manera sencilla. Aunque también hay que tener en cuenta que se reduce la comprensibilidad del código y se hace necesario el uso de herramientas de monitorización más avanzadas para lograr una trazabilidad completa del sistema.

Pero más allá de los aspectos técnicos de escalabilidad, flexibilidad o desacoplamiento, los eventos de dominio representan sucesos que ocurren en el dominio, por lo que ayudan a implementar un modelo rico y expresivo que refleja el lenguaje del negocio. Esto facilita la comunicación entre los equipos al hablar explícitamente sobre cambios y acciones derivadas como parte intrínseca del negocio.

Un lenguaje común

Uno de los cambios que más importancia ha tenido en la implantación de la filosofía Domain-Driven Design es precisamente la de mantener un lenguaje común entre todos los equipos, desde negocio hasta desarrollo de producto. Y a la vez puede ser uno de los cambios que más desapercibido suelen pasar.

No os podéis imaginar la cantidad de tiempo que hemos perdido cada vez que salía a relucir el concepto de “genially privado” en una conversación. Un concepto que no tenía una representación explícita en código ya que nuestro código no “hablaba” en los mismos términos que nuestro negocio.

A nivel de código, un genially tiene una serie de atributos que solo el equipo técnico conoce, como un atributo para indicar si es indexable por los motores de búsqueda. La combinación de este tipo de atributos con unos valores concretos es lo que realmente definía que un genially es privado.

Imaginad esas conversaciones donde, para más inri, el concepto de “genially privado” evoluciona porque el propio concepto de genially también cambia. Nuestra cabeza continuamente tenía que realizar “mapping mentales” para determinar todos los atributos involucrados y la combinación de valores. Que además no éramos capaces de transmitir de manera efectiva al resto de equipos. Una locura.

Todo cambió en el momento en el que esos atributos quedaron relegados al modelo de datos y nuestro modelo de dominio empezó a “hablar” explícitamente sobre si un genially es privado o no. Sin más.

Situaciones parecidas a esta hemos vivido con otros conceptos como “upgrade” y “update”. Conceptos que para nuestro negocio tenían matices distintos y que para nuestro equipo eran sinónimos porque de manera inconsciente asociamos esos verbos al “update” de una API CRUD. Hasta que de repente algo falla en producción y nadie sabe por qué el sistema no se comporta como se esperaba.

Por tanto, en nuestro caso, una parte importante del desarrollo ahora pasa por definir un glosario de términos (aunque no siempre se plasma en un documento) y tenerlo muy presente por si hay que modificar el significado de alguno de ellos, o si por hubiera que eliminar o añadir nuevos. Esto hace que nos “salten las alarmas” si alguien comienza a utilizar un concepto al que no estamos habituados.

Una vez más, esto nos ha llevado a tener una mejor alineación con el resto de equipos. Y, por supuesto, a una comprensión más precisa de nuestro dominio y de nuestro negocio.

Código más expresivo y “aburrido”

Así, uno de los objetivos del modelado enriquecido del dominio es que el lenguaje ubicuo tenga su reflejo en el propio código. En consecuencia el código se vuelve más expresivo y es más sencillo de entender, mantener y evolucionar.

Además, si sumamos a que lo habitual es implementar los patrones tácticos de Domain-Driven Design mediante arquitecturas limpias nos da un plus en cuanto a eficiencia. Este tipo de arquitecturas definen claramente una separación de responsabilidades entre las distintas capas de infraestructura, aplicación y dominio, así como separación en módulos en base a conceptos relacionados. Como, por ejemplo, puede ser el módulo de gestión de geniallys o el módulo de gestión de carpetas.

Con este tipo de arquitecturas, desarrollar un nuevo caso de uso es casi un “copia y pega” puesto que el flujo de desarrollo es muy similar (obviamente más allá de las propias reglas de negocio que cambian de un caso de uso a otro). Si conoces o has trabajado con este tipo de arquitecturas, prácticamente eres capaz de “tocar” cualquier base de código que implemente una arquitectura similar.

Justamente este último aspecto es algo que hemos notado en los onboardings. Hemos visto cómo se ha reducido el tiempo que tarda una nueva persona del equipo en ser productiva y llevar su primer código a producción. Aunque obviamente siempre están apoyados por el resto del equipo.

Y esto es solo el comienzo

Aunque pueda parecer que somos ya expertos en Domain-Driven Design, que hacemos código “como churros” y que ya “nos hemos pasado el juego”. Nada más lejos de la realidad. Este es un camino que apenas estamos empezando a recorrer. Lo bueno es que sabemos hacia dónde vamos. Nuestro objetivo es siempre el mismo: reducir la complejidad accidental y centrarnos en la complejidad esencial. O dicho de otra manera: poner nuestro negocio en el centro de todo frente a los aspectos técnicos.

Aún tenemos mucho por explorar con respecto a los patrones estratégicos de Domain-Driven Design de los que apenas hemos hablado (por no decir que no hemos mencionado nada). Al final estos patrones son los que tienen un impacto más profundo, aunque también son los más complicados de llevar a cabo ya que suponen un alineamiento total entre todas las áreas de la empresa.

Ojalá nuestra experiencia y aprendizaje pueda servir a que otros equipos se animen a transitar nuestro mismo camino, o un camino similar. En Genially creemos firmemente en que es un viaje que merece la pena comenzar. Eso sí, siempre teniendo en cuenta el momento y las necesidades del negocio, porque será parte importante del éxito.

--

--