🕒 4 minutos de lectura

En el artículo anterior hablábamos de Clean Architecture y Arquitectura Hexagonal desde un punto de vista conceptual: qué problema resuelven, cuándo usarlas y cuándo no.
Pero hay algo que siempre ayuda más que cualquier teoría: ver ejemplos reales.
En este artículo voy a mostrar el mismo problema resuelto con ambos enfoques, usando ejemplos sencillos y realistas, sin frameworks concretos ni estructuras artificiales.
El objetivo no es que copies una carpeta u otra, sino que entiendas la idea detrás de cada arquitectura.
El problema que vamos a resolver
Imaginemos una aplicación muy común:
👉 Una tienda online donde se pueden crear pedidos
Las reglas básicas son simples:
- Un pedido tiene varios productos.
- El precio final es la suma de los productos.
- El pedido se guarda en algún sistema de persistencia.
Nada más. Nada de microservicios, colas ni integraciones raras.
Ejemplo con Clean Architecture
Clean Architecture parte siempre de la misma pregunta:
¿Cuál es la lógica de negocio pura de este sistema?
Y empieza desde el centro, no desde el framework.
Dominio: la lógica de negocio
Aquí vive lo importante. No hay anotaciones, no hay infraestructura.
class Order {
private List<Item> items;
double calculateTotal() {
return items.stream()
.mapToDouble(Item::price)
.sum();
}
}
Esta clase:
- No sabe cómo se guarda el pedido.
- No sabe si hay una API REST.
- No sabe qué framework se usa.
👉 Solo representa el negocio.
Caso de uso: qué hace el sistema
Ahora definimos qué acción puede realizar el sistema.
class CreateOrderUseCase {
private final OrderRepository orderRepository;
CreateOrderUseCase(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
void execute(Order order) {
orderRepository.save(order);
}
}
Puntos clave:
- El caso de uso depende de una interfaz, no de una implementación.
- No hay dependencias técnicas.
- Es fácil de testear.
Infraestructura: un detalle intercambiable
Aquí es donde aparece la base de datos, framework, etc.
class JpaOrderRepository implements OrderRepository {
public void save(Order order) {
// Persistencia con JPA
}
}
Este código puede cambiar sin tocar el dominio ni los casos de uso.
🧠 Qué aporta Clean Architecture en este ejemplo
- La lógica está protegida.
- Los tests son simples.
- El framework no domina el diseño.
- El sistema puede evolucionar con menos riesgo.
Ejemplo con Arquitectura Hexagonal
Ahora resolvemos el mismo problema, pero pensando de otra forma.
En Hexagonal, la pregunta clave es:
¿Cómo entra información al sistema y cómo sale?
El núcleo de la aplicación
class OrderService {
void createOrder(Order order) {
// lógica de negocio
}
}
Este núcleo:
- No sabe si alguien llama por HTTP.
- No sabe si se guarda en una base de datos.
- No depende de nada externo.
Puerto de entrada (Input Port)
Define qué se puede hacer desde fuera.
interface CreateOrderPort {
void createOrder(Order order);
}
Adaptador de entrada (por ejemplo REST)
class OrderController {
private final CreateOrderPort port;
OrderController(CreateOrderPort port) {
this.port = port;
}
void create(OrderDto dto) {
port.createOrder(dto.toDomain());
}
}
Aquí entra la información al sistema.
Puerto de salida (Output Port)
interface OrderPersistencePort {
void save(Order order);
}
Adaptador de salida (DB, API externa, etc.)
class OrderJpaAdapter implements OrderPersistencePort {
public void save(Order order) {
// persistencia
}
}
🧠 Qué aporta Hexagonal en este ejemplo
- El núcleo está totalmente aislado.
- Cambiar REST por eventos no afecta al negocio.
- Cambiar la DB no rompe el sistema.
- El diseño es muy claro a nivel de dependencias.
Ejemplo de lo que NO es ni Clean ni Hexagonal
Este código es muy común… y muy problemático:
@RestController
class OrderController {
@Autowired
OrderRepository repository;
@PostMapping("/orders")
void create(OrderEntity entity) {
// lógica de negocio aquí
repository.save(entity);
}
}
Problemas:
- El controlador hace demasiadas cosas.
- La lógica depende de la persistencia.
- No hay separación real.
- Testear esto es difícil.
Este tipo de código aparece cuando:
👉 usamos el nombre del patrón, pero no su idea.
Lo que ocurre en proyectos reales (y está bien)
En la práctica, muchos sistemas bien diseñados:
- Usan casos de uso claros (Clean Architecture)
- Junto con puertos y adaptadores (Hexagonal)
- Sin obsesionarse con nombres o carpetas exactas
Eso no es un error.
Eso es arquitectura madura.
Conclusión
Clean Architecture y Arquitectura Hexagonal no son recetas mágicas. Son formas distintas de pensar el mismo problema.
Si después de aplicar uno de estos enfoques consigues que:
- Tu dominio esté protegido,
- Las dependencias estén bien dirigidas,
- El sistema pueda cambiar sin romperse,
entonces has elegido bien.
El nombre del patrón es lo de menos. La idea detrás, lo es todo.
