Domain-Driven Design en Java: no solo teoría, sino código vivo

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Por qué DDD no es un término de moda, sino una herramienta para salvar proyectos



Cada desarrollador Java se ha enfrentado alguna vez a esta situación: el código funciona, la lógica de negocio está dispersa por los servicios, y después de seis meses, solo el autor (y con dificultad) puede entender el proyecto. ¿Le suena familiar? Este es el clásico modelo de dominio anémico — donde las entidades son solo POJO con getters y setters, y toda la lógica vive en servicios "gordos".



Aquí entra en escena Domain-Driven Design (DDD) — un enfoque que pone la lógica de negocio en el centro del desarrollo, no los detalles técnicos. Y no, no es otra metodología "teórica". DDD en proyectos Java proporciona patrones concretos que transforman el caos en una arquitectura limpia y mantenible. En este artículo, exploraremos DDD en la práctica: con código, ejemplos y sin rodeos.



DDD Estratégico vs Táctico: ¿qué elegir para un proyecto Java?



DDD se divide en dos niveles: estratégico y táctico. El primero ayuda a dividir un sistema grande en Contextos Delimitados (Bounded Contexts) y a establecer la comunicación entre ellos. El segundo proporciona bloques de construcción concretos para el código: Entity, Value Object, Aggregate, Repository, Domain Event, etc.



Para un desarrollador Java, el DDD táctico es por dónde empezar. Proporciona herramientas que se pueden aplicar hoy mismo. Pero sin una comprensión estratégica de los contextos, corre el riesgo de crear un modelo "universal" que no se adapta a nadie. Por lo tanto, comencemos con lo básico: analicemos los patrones clave con el ejemplo de un dominio simple pero real: un sistema de gestión de pedidos.



Bloques de construcción de DDD en Java: de Entity a Aggregate



1. Entity (Entidad) — cada una tiene su propia identidad



En DDD, una Entity es un objeto que tiene un identificador único y una línea de vida continua. Puede cambiar todos sus campos, pero seguirá siendo la misma entidad. En Java, esto no es solo un POJO, es un objeto con comportamiento.



Ejemplo: un pedido en una tienda en línea. Incluso si el estado del pedido cambia, sigue siendo el mismo pedido con el mismo ID.



public class Order {    private final OrderId id; // Value Object, no un primitivo    private OrderStatus status;    private List<OrderLineItem> items;    private Money totalAmount;

public Order(OrderId id) { this.id = id; this.status = OrderStatus.PENDING; this.items = new ArrayList<>(); this.totalAmount = Money.ZERO; }

public void addItem(Product product, int quantity) { // Regla de negocio: no se puede agregar un artículo a un pedido confirmado if (this.status != OrderStatus.PENDING) { throw new IllegalStateException("Cannot add items to a non-pending order"); } OrderLineItem item = new OrderLineItem(product, quantity); items.add(item); recalculateTotal(); }

public void confirm() { if (items.isEmpty()) { throw new IllegalStateException("Cannot confirm an empty order"); } this.status = OrderStatus.CONFIRMED; // Aquí se puede lanzar un evento de dominio // registerEvent(new OrderConfirmedEvent(this.id)); }

private void recalculateTotal() { this.totalAmount = items.stream() .map(OrderLineItem::getSubtotal) .reduce(Money.ZERO, Money::add); }

// Getters — solo de lectura, ¡sin setters! public OrderId getId() { return id; } public OrderStatus getStatus() { return status; } public List<OrderLineItem> getItems() { return Collections.unmodifiableList(items); } public Money getTotalAmount() { return totalAmount; }}


Tenga en cuenta: OrderId no es un Long, sino un Value Object. ¿Por qué? Porque un tipo primitivo no transmite semántica. OrderId puede contener un prefijo, una fecha de creación o pro

Blogs

Book Recommendations