Diseño de software
La estructura que refleja el negocio: modularidad, cohesión y acoplamiento
¿Por qué modularizar solo por afinidad técnica, si los elementos de negocio son los verdaderos protagonistas de la aplicación?
Cristiano Rego Barros
6 min de lectura
Publicado originalmente en Medium
Hoy en día, con conceptos como DDD (Domain-Driven Design), microservicios y Clean Architecture cada vez más en auge, se habla mucho de modularidad. Sin embargo, surgen algunas dudas a su alrededor, como:
- ¿Qué significa realmente la modularidad?
- ¿La modularidad es solo técnica?
- ¿Cómo puedo construir una aplicación modular?
Son preguntas que muchas veces no se explican, y es necesario buscar ese entendimiento para comprender cómo lograrla. Como ya decía Glenford Myers en 1978:
«El noventa y cinco por ciento de las palabras [sobre arquitectura de software] se usan para alabar los beneficios de la ‘modularidad’ y pocas, si acaso alguna, tratan de cómo lograrla.»
— Glenford J. Myers (1978)
Para ayudar con este tema, intento explicarlo un poco en este breve artículo.
Qué es la modularidad
El diccionario define la modularidad como la cualidad de lo que es modular o está formado por módulos. Por su parte, Eric Evans, autor de Domain-Driven Design, afirma que los módulos son elementos de diseño consolidados, entre los cuales debe haber bajo acoplamiento y alta cohesión interna.
Cohesión
La cohesión puede entenderse como el nivel de relación entre los elementos internos de un módulo. Es decir, un módulo cohesivo agrupa componentes que tiene sentido que estén juntos, porque colaboran con el mismo objetivo o responsabilidad. Cuanto más relacionados estén esos componentes entre sí, más cohesivo es el módulo.
En la imagen de abajo tenemos un grafo ilustrativo con tres agrupaciones:
- Nube: elementos 1 a 5;
- Estrella: elementos 6 a 8;
- Elemento 9 aislado.
Se podrían pensar distintas formas de agrupar o modularizar los nodos del grafo anterior, por ejemplo:
- Nodos pares e impares;
- Nodos con y sin relaciones, etc.
Sin embargo, como no hay ningún contexto descrito, se puede afirmar que, visualmente, la división mencionada antes es la que genera módulos con elementos más conectados, es decir, el formato más cohesivo.
Esta búsqueda de los mejores motivos (o las mejores características) para agrupar elementos de diseño es lo que da valor a tus módulos para que sean cohesivos.
Acoplamiento
El acoplamiento, a su vez, puede caracterizarse como el nivel de dependencia entre distintos elementos.
Así, hablar de elementos con alto acoplamiento entre sí equivale a hablar de una gran dependencia entre ellos. Se entiende, entonces, que los elementos con bajo acoplamiento son aquellos en los que se promueve la independencia.
Al analizar la ilustración de abajo, se nota que los elementos del Módulo A no tienen ninguna relación ni dependencia con elementos de los demás módulos. En cambio, los elementos de B están en relación y dependencia frecuente con el elemento de C, lo que genera acoplamiento entre módulos.
Estas asociaciones frecuentes pueden hacer que los elementos de B se vuelvan dependientes e incluso rehenes de los cambios causados por los elementos de C. En ese caso, los módulos empiezan a volverse frágiles, ya que no cubren sus propias necesidades. Por eso se buscan módulos desacoplados e independientes, para que sean módulos fuertes.
Dentro de un módulo cohesivo, el acoplamiento entre sus elementos se vuelve más aceptable, porque esos elementos están conectados y trabajan en torno a un mismo objetivo; es decir, los cambios en una pieza del módulo muy probablemente serán válidos para las demás.
Por otro lado, al acoplarse a elementos de módulos no cohesivos o entre módulos distintos, los cambios en un elemento muy probablemente no serán relevantes para los demás, lo que puede generar problemas en las relaciones. Por eso es importante buscar módulos cohesivos y desacoplados y, del mismo modo, huir de módulos acoplados y desconectados.
Modularidad por capas
Como afirman Richards y Ford:
«Los sistemas de software modelan sistemas complejos, que tienden a la entropía (o al desorden).»
— Mark Richards y Neal Ford
A partir de este principio, se destaca la importancia de buscar siempre una organización clara y cohesiva en los sistemas de software, para lograr un proyecto mantenible y evolutivo, al contrario de la tendencia casual, en la que la organización del código entorpece la evolución del sistema.
En el software moderno, lo habitual es dividir componentes y artefactos según su diferenciación técnica. Así surge la arquitectura en capas, en la que los componentes internos de un software se organizan según su responsabilidad técnica, creando capas como:
- Capa de presentación (controllers, consumidores);
- Capa de aplicación (casos de uso, clases de servicio);
- Capa de infraestructura (mapeo de base de datos, gestión de conexiones con drivers externos).
Pero ¿estos grupos de componentes (o módulos) son realmente cohesivos y desacoplados? ¿Basta con este formato de división para tener un código limpio y claro?
A primera vista, esta división parece tener sentido, ya que los elementos comparten una responsabilidad técnica común. Sin embargo, al adoptar una división en capas, es común caer en el escenario de la imagen de abajo, en el que cada capa tiene distintos elementos que hacen las mismas tareas, pero para dominios diferentes.
Imagina que hay límites estrictos entre dominios, de modo que los elementos de un dominio de negocio no conversan con los de otro y solo se relacionan con elementos de su mismo contexto. Las relaciones entre elementos y componentes de la aplicación se verán como en la imagen de abajo:
De esta forma, se ve que hay un gran volumen de comunicación entre módulos y ninguna dentro de ellos. Esto ocurre porque los elementos que ejecutan una tarea no se relacionan (o no deberían relacionarse) con otros del mismo tipo: un lápiz no se relaciona con otro lápiz, sino con un papel o con la mano de quien escribe. Sus relaciones están definidas por su objetivo de negocio; por ejemplo, los elementos necesarios para que alguien escriba un libro.
Modularidad por dominio
Como vimos en el apartado anterior, la forma casual de modularizar por capas técnicas no termina siendo la mejor opción e incluso puede convertirse en una trampa en ciertos contextos. Surge entonces la modularización por dominios de negocio, muy influenciada por conceptos del DDD (Domain-Driven Design).
Básicamente, la idea detrás de esta forma de modularidad es dividir los componentes de la aplicación según sus capacidades de negocio, para obtener módulos cohesivos y desacoplados. Una representación de este concepto sigue la línea de la imagen de abajo:
Esta estrategia resulta muy conveniente para proyectos iniciales que apuntan a una arquitectura de microservicios. En esa arquitectura, una de las mayores dificultades y dudas gira en torno a la granularidad correcta de los servicios. Al inicio del proyecto, cuando se sabe poco del dominio, una arquitectura modularizada por dominio puede ser una excelente alternativa.
El proyecto puede empezar con pocos servicios, o incluso uno solo, dividido internamente por dominios. A medida que cada uno se vuelve relativamente robusto, el módulo entero puede extraerse y trasladarse a otro servicio, en otra base de código, sin impactar a los demás ni verse impactado, ya que pasa a seguir su propio ciclo de vida de forma independiente.
Además, nada impide que cada módulo de dominio se organice internamente por capacidades técnicas. Se obtiene así una estructura híbrida: organizada en capas, pero modularizada por dominios de negocio, algo similar a la imagen de abajo:
Este enfoque no solo promueve una cohesión y un desacoplamiento reales, sino que también abre el camino hacia una arquitectura más alineada con el negocio. Para profundizar en este tipo de organización modular, se recomienda estudiar Domain-Driven Design (DDD), que trata con más profundidad la identificación y la separación de dominios de forma estratégica.
Conclusión
En resumen, la modularidad va mucho más allá de dividir el código en capas técnicas. Se trata de alinear estructura y propósito, garantizando que la organización del software refleje la lógica del negocio que representa. Adoptar una modularización orientada a dominios es un paso sólido hacia sistemas más cohesivos, escalables y con menor costo de mantenimiento a lo largo del tiempo.
Si ya enfrentaste desafíos con la modularidad en tus proyectos o tienes experiencias distintas sobre cómo organizar tu código, cuéntame. Sigamos este debate e intercambiemos aprendizajes sobre cómo hacer nuestras aplicaciones más sostenibles y alineadas con el negocio.