¿Qué es DDD o Domain-Driven Design?
DDD (Domain-Driven Design) es el enfoque de desarrollo de software creado por Eric Evans que centra el diseño en el dominio de negocio. Aprende Bounded Context, Aggregates, Ubiquitous Language y patrones estratégicos y tácticos.
Definición
Domain-Driven Design (DDD), o Diseño Guiado por el Dominio, es un enfoque de desarrollo de software que coloca el dominio de negocio en el centro de todas las decisiones de diseño y arquitectura. Fue conceptualizado por Eric Evans en su libro "Domain-Driven Design: Tackling Complexity in the Heart of Software" publicado en 2003, y desde entonces se ha convertido en una referencia fundamental para abordar la complejidad en sistemas de software empresarial.
La premisa central de DDD es que la complejidad principal del software no reside en la tecnología, sino en el dominio de negocio que intenta resolver. Por tanto, el mayor esfuerzo del equipo de desarrollo debe enfocarse en comprender profundamente ese dominio y reflejar ese conocimiento fielmente en el código.
DDD promueve una colaboración estrecha entre expertos del dominio (personas que conocen el negocio) y expertos técnicos (desarrolladores, arquitectos) para construir un modelo compartido que represente con precisión los conceptos, reglas y procesos del negocio. Este modelo se convierte en el corazón del software y evoluciona junto con la comprensión del dominio.
Características
DDD se estructura en dos grandes áreas: los patrones estratégicos y los patrones tácticos.
Patrones estratégicos
Los patrones estratégicos abordan la organización del sistema a gran escala:
-
Ubiquitous Language (Lenguaje Ubicuo): un vocabulario compartido entre desarrolladores y expertos del dominio que se usa de forma consistente en conversaciones, documentación y código. Si el negocio habla de "Pedidos", el código usa
Pedido, noOrderniRequest. -
Bounded Context (Contexto Delimitado): un límite explícito dentro del cual un modelo de dominio particular tiene significado. Diferentes partes de una organización pueden usar los mismos términos con significados distintos; el Bounded Context define dónde cada definición aplica.
-
Context Map (Mapa de Contextos): una representación visual de las relaciones entre los diferentes Bounded Contexts de un sistema, identificando cómo se integran y comunican entre sí.
-
Subdomain (Subdominio): división del dominio completo en áreas más manejables, clasificadas como:
- Core Domain: la ventaja competitiva, donde se invierte más esfuerzo.
- Supporting Domain: necesario pero no diferenciador.
- Generic Domain: problemas ya resueltos (autenticación, email).
Patrones tácticos
Los patrones tácticos definen los bloques de construcción del modelo dentro de un Bounded Context:
- Entity (Entidad): objeto con identidad única que persiste a lo largo del tiempo (ej: un
Clientecon su ID). - Value Object (Objeto de Valor): objeto sin identidad propia, definido por sus atributos (ej: una
DirecciónPostal). - Aggregate (Agregado): grupo de entidades y objetos de valor que se tratan como una unidad de consistencia.
- Aggregate Root (Raíz del Agregado): la entidad principal que controla el acceso al agregado.
- Repository (Repositorio): abstracción para persistir y recuperar agregados.
- Domain Service (Servicio de Dominio): lógica de negocio que no pertenece naturalmente a ninguna entidad.
- Domain Event (Evento de Dominio): algo significativo que ocurrió en el dominio y que otros componentes necesitan conocer.
- Factory (Factoría): encapsula la lógica compleja de creación de agregados.
Ejemplo práctico
Consideremos una plataforma de comercio electrónico. Sin DDD, un desarrollador podría crear un modelo anémico con una clase Order que es básicamente un contenedor de datos, con toda la lógica en servicios externos.
Con DDD, el diseño cambia radicalmente:
Identificación de Bounded Contexts:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ Catálogo │ │ Pedidos │ │ Envíos │ │ │ │ │ │ │ │ - Producto │ │ - Pedido │ │ - Envío │ │ - Categoría │ │ - LíneaPedido │ │ - Paquete │ │ - Precio │ │ - Descuento │ │ - Seguimiento │ └─────────────────┘ └──────────────────┘ └─────────────────┘
Modelo de dominio del contexto "Pedidos":
// Aggregate Root public class Pedido { private PedidoId id; private ClienteId clienteId; private List<LineaPedido> lineas; private EstadoPedido estado; private Dinero totalCalculado; // La lógica de negocio vive DENTRO del dominio public void agregarProducto(ProductoId producto, Cantidad cantidad, Precio precio) { if (this.estado != EstadoPedido.BORRADOR) { throw new PedidoNoModificableException(this.id); } if (cantidad.valor() <= 0) { throw new CantidadInvalidaException(); } this.lineas.add(new LineaPedido(producto, cantidad, precio)); this.recalcularTotal(); } public void confirmar() { if (this.lineas.isEmpty()) { throw new PedidoVacioException(); } this.estado = EstadoPedido.CONFIRMADO; // Emite un evento de dominio this.registrarEvento(new PedidoConfirmado(this.id, this.totalCalculado)); } private void recalcularTotal() { this.totalCalculado = this.lineas.stream() .map(LineaPedido::subtotal) .reduce(Dinero.ZERO, Dinero::sumar); } } // Value Object - inmutable, sin identidad public record Dinero(BigDecimal cantidad, Moneda moneda) { public Dinero sumar(Dinero otro) { if (!this.moneda.equals(otro.moneda)) { throw new MonedasIncompatiblesException(); } return new Dinero(this.cantidad.add(otro.cantidad), this.moneda); } }
Lenguaje Ubicuo en acción:
El Product Owner dice: "Cuando un cliente confirma un pedido, se debe verificar que no esté vacío y calcular el total incluyendo descuentos".
El código refleja exactamente ese lenguaje: pedido.confirmar() verifica que no esté vacío y el total se recalcula automáticamente. No hay traducción entre el lenguaje del negocio y el código.
¿Por qué es importante?
DDD es especialmente relevante en contextos donde la complejidad del dominio es alta:
Gestión de la complejidad: DDD proporciona herramientas para dividir un dominio complejo en partes manejables (Bounded Contexts, Subdominios) y modelar cada parte con la profundidad necesaria. Esto evita el antipatrón del "Big Ball of Mud" donde todo está acoplado.
Alineación negocio-tecnología: el Lenguaje Ubicuo elimina la brecha de comunicación entre equipos técnicos y de negocio. Cuando ambos usan los mismos términos con los mismos significados, se reducen los malentendidos y el software refleja mejor las necesidades reales.
Evolución sostenible: los sistemas diseñados con DDD son más fáciles de evolucionar porque los cambios de negocio se traducen directamente en cambios en el modelo de dominio, que está claramente delimitado por Bounded Contexts. Cambiar un contexto no debería afectar a otros.
Base para microservicios: los Bounded Contexts proporcionan límites naturales para definir microservicios. Cada Bounded Context puede convertirse en un servicio independiente con su propia base de datos, equipo y ciclo de despliegue.
Mejor calidad del código: los patrones tácticos de DDD (entidades, value objects, agregados) producen código más expresivo, testeable y mantenible. La lógica de negocio queda encapsulada en el modelo de dominio en lugar de dispersarse por toda la aplicación.
Cuándo usar DDD
DDD es valioso cuando:
- El dominio de negocio es complejo y tiene reglas sofisticadas.
- El equipo tiene acceso a expertos del dominio.
- El sistema tiene una vida útil larga y necesita evolucionar.
- Múltiples equipos trabajan en diferentes partes del sistema.
DDD puede ser excesivo cuando:
- El dominio es simple (CRUD básico).
- El proyecto es pequeño o de corta duración.
- No hay acceso a expertos del dominio.
Preguntas frecuentes
¿Cuál es la diferencia entre DDD, TDD y BDD?
DDD se centra en el diseño del software basado en el dominio de negocio. TDD (Test-Driven Development) es una práctica de escribir pruebas antes del código. BDD (Behavior-Driven Development) define el comportamiento esperado en lenguaje natural. Son complementarios: se puede usar TDD/BDD para implementar un modelo DDD.
¿DDD es solo para proyectos grandes?
Los patrones estratégicos de DDD (Bounded Contexts, Lenguaje Ubicuo) son valiosos en cualquier proyecto. Los patrones tácticos (Entities, Aggregates) aportan más valor en dominios complejos. No es necesario adoptar todo DDD; se pueden aplicar selectivamente los patrones que más beneficien al proyecto.
¿Qué relación tiene DDD con los microservicios?
Los Bounded Contexts de DDD proporcionan los límites naturales para definir microservicios. Sin embargo, DDD no requiere microservicios ni viceversa. Se puede aplicar DDD en un monolito modular. La clave es que DDD ofrece las herramientas conceptuales para decidir dónde dibujar los límites del servicio.
¿Qué es un Aggregate Root y por qué es importante?
El Aggregate Root es la entidad principal que controla el acceso a un grupo de objetos relacionados (el Agregado). Toda modificación del agregado pasa a través de su raíz, garantizando las invariantes de negocio. Por ejemplo, un Pedido es el Aggregate Root que controla el acceso a sus LineasPedido.
¿Cómo empiezo con DDD en un proyecto existente?
El enfoque recomendado es empezar por los patrones estratégicos: identificar los Bounded Contexts del sistema actual, establecer un Lenguaje Ubicuo con los expertos del dominio y crear un Context Map. Luego, aplicar patrones tácticos progresivamente en las áreas de mayor complejidad o donde se planeen cambios significativos. No es necesario reescribir todo el sistema.
¿Event Storming es parte de DDD?
Event Storming es una técnica de workshop colaborativa creada por Alberto Brandolini que complementa DDD. Se utiliza para descubrir los eventos del dominio, los procesos de negocio y los Bounded Contexts de forma visual y participativa. Aunque no es formalmente parte de DDD, se ha convertido en una herramienta estándar para la fase de descubrimiento del dominio.
¿Quieres saber más?
Si te interesa saber más acerca de DDD - Domain-Driven Design (Diseño Guiado por el Dominio), escríbeme por linkedin. Me encanta compartir ideas, dudas y curiosidades sobre estos temas, así que no dudes en pasarte por ahí. ¡Nos leemos!
¿Qué significa Three Amigos?
"Three Amigos" o "Los Tres Amigos" es un término utilizado en el desarrollo...
¿Qué es la deuda técnica?
La deuda técnica es el resultado de elegir una solución rápida sobre una má...
¿Qué es una Release Candidate?
La Release Candidate o (RC) es una versión de un programa de software que e...
¿Qué es un Pull Request (PR)?
Un Pull Request (PR), también conocido como Merge Request (MR) en GitLab, e...
¿Qué significa QA?
Quality Assurance (QA) es el proceso sistemático de asegurar que el softwar...