Nivel 2 · 25 min
Clean Architecture
Clean Architecture de Uncle Bob organiza el código en capas concéntricas con una regla central: las dependencias apuntan solo hacia adentro. El núcleo del dominio no conoce nada de la infraestructura —ni frameworks, ni bases de datos, ni HTTP.
La Regla de Dependencia
Las capas, de interior a exterior: Entities (reglas de negocio enterprise), Use Cases (reglas de negocio de aplicación), Interface Adapters (controllers, presenters, repositories), Frameworks & Drivers (Spring, Hibernate, HTTP). Una capa exterior puede conocer la interior, pero nunca al revés.
Use Case Interactors
Los Use Cases orquestan entities para lograr un objetivo de negocio específico. Cada use case tiene exactamente un propósito: PlaceOrderUseCase, CancelOrderUseCase. No tienen dependencias de framework —sus dependencias son interfaces (puertos) que la infraestructura implementa. Esto hace los use cases completamente testeables sin infraestructura.
Interface Adapters
Los Interface Adapters traducen entre el lenguaje de los use cases (DTOs internos) y el lenguaje externo (HTTP request/response, SQL, JSON). Un Repository implementa la interfaz que el use case define y traduce entre el modelo de dominio y el esquema de base de datos.
Code example
// Entity (núcleo —sin dependencias)
class Order {
void confirm() {
if (items.isEmpty()) throw new DomainException("Order must have items");
this.status = OrderStatus.CONFIRMED;
}
}
// Use Case (depende solo de interfaces)
class ConfirmOrderUseCase {
private final OrderRepository orders;
private final PaymentGateway payments;
ConfirmOrderResult execute(ConfirmOrderCommand cmd) {
Order order = orders.findById(cmd.orderId()).orElseThrow();
order.confirm();
payments.charge(order.total(), cmd.paymentDetails());
orders.save(order);
return new ConfirmOrderResult(order.id(), order.status());
}
}
// Adapter: implementa la interfaz
class JpaOrderRepository implements OrderRepository {
@Override
public Optional<Order> findById(OrderId id) {
return jpaRepo.findById(id.value()).map(OrderMapper::toDomain);
}
}