Clean Architecture

Idea central

Clean Architecture organiza código para que las políticas internas no dependan de detalles externos. Robert C. Martin la presenta como una síntesis de aproximaciones anteriores, incluyendo Hexagonal, Onion, Boundary-Control-Entity y DCI. Su regla central es la Dependency Rule: las dependencias de código fuente apuntan hacia políticas de mayor nivel. The Clean Architecture, 2012.

No es un certificado de calidad del sistema. Un diseño puede cumplir esa dirección y tener malas reglas de negocio, problemas operativos o abstracciones innecesarias. También puede necesitar más o menos límites que el dibujo habitual.

Responsabilidades, no carpetas obligatorias

El modelo conocido distingue cuatro zonas conceptuales:

ZonaResponsabilidad que representa
EntitiesPolíticas de negocio generales.
Use CasesReglas y coordinación específicas de la aplicación.
Interface AdaptersTraducción entre formas internas y mecanismos externos.
Frameworks and DriversTecnologías concretas, como UI, database y frameworks.

Las zonas y la regla se desarrollan en el artículo original; sus cuatro círculos son esquemáticos. El criterio de separación es qué decisión conoce cada parte, no mantener exactamente cuatro directorios.

En nuestro ejemplo propio, una Solicitud conoce sus transiciones válidas; AprobarSolicitud coordina el objetivo de aplicación. Un controller interpreta HTTP y un presenter construye la representación de la respuesta. Un adaptador de almacenamiento traduce el estado a persistencia.

El nombre presenter en este ejemplo de adaptación de salida no declara automáticamente MVP. presentation-patterns organiza interacción y estado de UI; puede coexistir con Clean, conservando sus reglas de dependencia. Los nombres de los participantes no prueban por sí solos el patrón de presentación.

Entity no equivale necesariamente a una fila ni a una clase sin comportamiento. Su papel se reconoce por las políticas que expresa. Un archivo llamado Entity que importa tipos del driver sigue introduciendo un detalle exterior. Use Case tampoco exige una clase por verbo: su implementación depende del lenguaje y del tamaño real de la responsabilidad.

Dependencias de código frente a llamadas

Dependencias de código en el ejemplo:
 
Controller → ApproveUseCase
Presenter  → ApproveOutputPort
SqlStore   → ApprovalStorePort
 
Flujo de ejecución posible:
 
Controller → ApproveUseCase → SqlStore
                          → Presenter

El caso de uso conoce contratos definidos en su frontera, no los tipos concretos exteriores. El flujo puede volver hacia un presenter sin invertir las dependencias del código. La guía de arquitecturas web de Microsoft distingue explícitamente referencias de compilación y colaboraciones resueltas en runtime.

En nuestro ejemplo, un punto externo de composición selecciona SqlStore. Si ApproveUseCase lo instancia directamente, vuelve a depender de esa implementación. Es una pregunta verificable sobre imports y referencias, no una propiedad deducible del dibujo.

Inversion of control, Dependency Inversion y Dependency Injection no son sinónimos. La primera describe control ejercido por otro componente; la segunda orienta dependencias respecto de abstracciones; la tercera suministra colaboradores. Fowler explica por qué IoC es demasiado amplio para nombrar únicamente el ensamblaje en su artículo sobre Dependency Injection. Usar un contenedor no demuestra la Dependency Rule; construir colaboradores manualmente puede conservarla.

Datos que cruzan fronteras

El interior debe recibir datos apropiados a su contrato, sin depender de formatos técnicos exteriores. El artículo original explica este límite y advierte sobre introducir tipos de filas de un framework en las políticas internas.

En nuestro ejemplo, la petición puede transformarse en identificador de solicitud y actor. No necesita conservar HttpRequest, cabeceras de transporte ni un objeto de sesión del ORM. El resultado del caso de uso puede representar aprobado, rechazo de negocio o conflicto; otro componente lo transforma en una respuesta de canal.

No se deduce que cada método deba tener un DTO nuevo ni que todo valor de dominio sea peligroso. Un objeto de valor interno puede ser adecuado si no filtra dependencias o autoridad que el contrato debe proteger. La revisión pregunta qué conocimiento atraviesa el límite, quién controla su formato y si el consumidor puede mutarlo de manera indebida.

Independencia y pruebas

Las reglas de aprobación pueden probarse sin servidor HTTP. El caso de uso puede ejercitarse con colaboradores que simulen ausencia de datos o conflictos. Los adaptadores necesitan pruebas propias con mecanismos reales para demostrar mapping, restricciones y traducción de errores.

Independencia de framework, UI y database significa reducir conocimiento técnico en las políticas. No significa que una aplicación real pueda funcionar sin sus recursos ni que cambiar tecnología sea gratuito. El contrato puede exigir atomicidad, orden o durabilidad que una implementación nueva debe satisfacer.

En nuestro ejemplo, pruebas aisladas que suponen Save siempre exitoso dejan sin verificar dos aprobaciones concurrentes. Una frontera bien orientada permite ubicar la prueba adecuada, pero no elimina ese riesgo. testability distingue control, observación y fidelidad de la evidencia; el nombre Clean no demuestra satisfacer ese escenario.

portability permite evaluar esa independencia frente a un destino concreto: cambios de plataforma localizados y comportamiento preservado. La Dependency Rule puede proteger políticas, pero la aplicación todavía necesita implementaciones exteriores que satisfagan sus contratos y controles de operación.

Costos y relaciones

Las fronteras agregan traducciones y contratos. Si se repiten datos y delegaciones sin proteger una diferencia significativa, el costo puede superar el beneficio. La dirección inward tampoco impide que un núcleo concentre todas las reglas del sistema y tenga alto acoplamiento interno.

ports-and-adapters enfatiza interacciones de frontera; onion-architecture sitúa el dominio en el centro. Clean ofrece una organización explícita de políticas y casos de uso. internal-structure compara ese parentesco sin presentar tres nombres como escalones de madurez.

Puede existir dentro de un monolith o un servicio de microservices. También puede agrupar los Use Cases por vertical-slice-architecture, manteniendo las dependencias elegidas. Una implementación layered-architecture con inversión puede compartir esas reglas. En todos los casos, documentar qué responsabilidades están protegidas y cómo se comprueba la frontera resulta más útil que imponer Entities/UseCases/Controllers/Repositories como definición.