Seguro que alguna vez te has topado con ese proyecto donde el código parece un plato de espaguetis y cualquier cambio mínimo provoca que todo el sistema se venga abajo. Cuando las aplicaciones crecen, la lógica de negocio suele mezclarse con la base de datos o el framework, convirtiendo el mantenimiento en una auténtica pesadilla. Aquí es donde entra en juego el Domain-Driven Design o DDD, una metodología que no se centra en la tecnología per se, sino en entender a fondo el problema que queremos resolver antes de escribir una sola línea de código.
Básicamente, el DDD nos propone un cambio de chip: en lugar de empezar diseñando tablas de bases de datos o pantallas, nos enfocamos en el corazón del negocio. Es como si necesitáramos un mapa muy detallado de una ciudad antes de empezar a poner ladrillos. Al alinear el software con la realidad operativa de la empresa, logramos que el sistema sea robusto, modular y, sobre todo, escalable, evitando que la complejidad técnica asfixie la agilidad del desarrollo.
Los pilares estratégicos del Diseño Guiado por el Dominio
Para no perdernos en el laberinto de un sistema complejo, el DDD introduce el concepto de Dominio, que es básicamente el área del problema que la organización necesita solucionar. Este dominio puede ser enorme, por lo que lo dividimos en subdominios. No todos tienen la misma importancia: hay un dominio central, que es donde reside el valor competitivo de la empresa, y dominios de soporte o genéricos que ayudan a que todo funcione pero no son el núcleo del negocio.
Una de las piezas más críticas es el Lenguaje Ubicuo. Se trata de crear un vocabulario común que utilicen tanto los programadores como los expertos del negocio. Si el experto llama a un proceso «Validación de Pedido», en el código debe aparecer una clase o método llamado exactamente así. Esto elimina los malentendidos y evita que el equipo técnico interprete las reglas de negocio a su antores, asegurando que la comunicación sea fluida y sin errores.
Para poner orden, utilizamos los Contextos Delimitados (Bounded Contexts). Imaginemos que en una tienda online, la palabra «Producto» significa una cosa para el equipo de ventas (precio, descripción) y otra muy distinta para el de logística (peso, dimensiones). En lugar de intentar crear un modelo único y gigante que sirva para todo, trazamos un círculo alrededor de cada área. Cada contexto tiene su propio modelo y reglas, lo que permite que los módulos estén débilmente acoplados y sean mucho más fáciles de gestionar.
Cuando estos contextos necesitan hablar entre sí, recurrimos al Mapa de Contexto. Es como el GPS del arquitecto, donde definimos cómo se integran los servicios y qué información fluye de un lado a otro. Esto es fundamental al trabajar con microservicios, ya que un contexto delimitado bien definido es el candidato perfecto para convertirse en un servicio independiente y autónomo.
Diseño Táctico: Bajando los conceptos al código
Una vez que tenemos la estrategia clara, pasamos a la acción con el diseño táctico. Aquí es donde definimos las Entidades, que son objetos con una identidad única que persiste en el tiempo (como un usuario con su ID), y los Objetos de Valor (Value Objects), que son inmutables y se definen solo por sus atributos (como una dirección de correo electrónico o una moneda). Usar objetos de valor nos permite evitar la «obsesión por los primitivos» y crear un código mucho más expresivo.
El concepto de Agregado (Aggregate) es vital para mantener la coherencia. Un agregado es un conjunto de entidades y objetos de valor que se tratan como una sola unidad. Dentro de este grupo hay una Raíz de Agregado (Aggregate Root), que es la única puerta de entrada permitida. Nadie puede modificar los elementos internos del agregado sin pasar por la raíz, lo que garantiza que las reglas de negocio se cumplan estrictamente y que las transacciones sean consistentes.
Para gestionar la lógica que no encaja en una entidad, existen los Servicios de Dominio. A diferencia de los servicios de aplicación, estos contienen reglas de negocio puras y no dependen de tecnologías externas. Por otro lado, los Repositorios actúan como el puente hacia la persistencia, pero ojo: en el dominio solo definimos la interfaz, mientras que la implementación real se deja para la capa de infraestructura, siguiendo el principio de inversión de dependencias.
También encontramos las Factories, encargadas de la creación compleja de objetos, y los Eventos de Dominio. Estos últimos son hechos que ya han ocurrido en el negocio (por ejemplo, «PedidoPagado») y sirven para notificar a otras partes del sistema que deben reaccionar, permitiendo que los microservicios se comuniquen de forma asíncrona y desacoplada.
La Arquitectura en Capas: Onion y Hexagonal
Para que el dominio no se ensucie con detalles técnicos, el DDD recomienda una arquitectura por capas. Ya sea la Arquitectura Onion o la Hexagonal (Puertos y Adaptadores), la regla de oro es que las dependencias siempre vayan hacia adentro. El núcleo es el Dominio, y nada en esa capa debe saber nada sobre bases de datos, frameworks de Android o APIs externas. De este modo, conseguimos que la lógica de negocio sea totalmente portable y fácil de testear.
La estructura típica se divide en cuatro niveles. Primero tenemos la Capa de Interfaz de Usuario, que gestiona las vistas y la interacción con el cliente. Luego está la Capa de Aplicación, que coordina los casos de uso y orquesta el trabajo del dominio, pero no contiene reglas de negocio. El corazón es la Capa de Dominio, donde residen las entidades y la lógica pura. Finalmente, la Capa de Infraestructura es la que implementa los detalles técnicos como la base de datos o el acceso a red.
Este enfoque es especialmente potente en Android cuando implementamos micro-frontends o módulos muy independientes. Al usar ViewModels para la capa de presentación, evitamos que las entidades de dominio se filtren hacia la UI, asegurando que los datos solo se muestren una vez que hayan sido validados por las raíces de los agregados. En esencia, estamos protegiendo el valor estratégico del negocio frente a la volatilidad de las librerías técnicas.
Para aquellos que se enfrentan a dominios extremadamente complejos, existen dinámicas como el Event Storming. Consiste en reunir a expertos de negocio y técnicos frente a un muro lleno de post-its para mapear eventos, comandos y políticas. Es una forma iterativa de descubrir los límites del contexto y los agregados basándose en el flujo real de los procesos, transformando una idea abstracta en un modelo ejecutable.
La adopción de estas prácticas permite que las aplicaciones Android no solo soporten un crecimiento masivo de usuarios, sino que también puedan evolucionar sin que el coste de mantenimiento se dispare. Al priorizar la comprensión del problema sobre la herramienta técnica, el software deja de ser un conjunto de tablas y funciones para convertirse en una representación fiel y flexible de la realidad empresarial, facilitando la implementación de microservicios que son realmente autónomos y coherentes.
Continúar leyendo...