Introducción
Cuando hablamos de patrones arquitectónicos en desarrollo de software, Clean Architecture suele asociarse principalmente con aplicaciones backend. Sin embargo, su aplicación en el frontend puede ser igualmente valiosa, especialmente en proyectos complejos o de larga duración.
En este artículo vamos a analizar cómo he implementado Clean Architecture en un proyecto real: una plataforma de turismo desarrollada con Next.js y Strapi.
¿Qué es Clean Architecture?
![[diagram-export-10-3-2025-20_22_51.png]]
Clean Architecture, propuesta por Robert C. Martin (Uncle Bob), se basa en un principio fundamental: la separación de responsabilidades mediante capas concéntricas donde las dependencias siempre apuntan hacia el centro. En el núcleo están las entidades y reglas de negocio, mientras que los detalles técnicos quedan en las capas exteriores.
La estructura por capas incluye:
- Dominio: Modelos (Zona, Grupo, Arranque...) y errores
- Aplicación: Casos de uso e interfaces de infraestructura (getLatestPostsUseCase, PostsRepository Interface)
- Adaptadores: Conexiones entre la lógica de negocio y el mundo exterior (getLatestPostsController, StrapiZonaAdapter)
- Frameworks e infraestructura: Implementaciones concretas (Next.js, APIs externas, bases de datos)
Estructura de nuestro proyecto
Nuestro proyecto sigue una clara separación en capas que combina Clean Architecture con Screaming Architecture y Vertical Slicing:
La regla de dependencia: el corazón de Clean Architecture
En mi config de ESLint he creado esta regla que obliga a seguir la ley de dependencia de Clean Architecture:
Esto garantiza que ninguna capa pueda importar nada de una capa externa:
Dominio: el núcleo de nuestra aplicación.
En esta capa definimos los modelos, cada una de las entidades que existirán en nuestra aplicación.
Las entidades contienen lógica de dominio pero no dependencias externas, lo que las hace independientes de frameworks o infraestructura.
Casos de uso: orquestando la lógica de negocio
Los casos de uso invocan a las implementaciones concretas a través de una interfaz:
Observa que los casos de uso:
- Reciben interfaces como dependencias (inversión de dependencias)
- No conocen implementaciones concretas, solo abstracciones
- Representan operaciones específicas de la aplicación
Infraestructura: implementaciones concretas
La capa de infraestructura implementa las interfaces definidas en la capa de aplicación:
Aquí hay detalles técnicos como llamadas HTTP, pero con una clara separación que permite cambiar de Strapi a cualquier otra fuente de datos sin afectar las capas internas.
Controladores: conectando con la UI
Los controladores son el puente entre la lógica de aplicación y la interfaz de usuario:
Los controladores:
- Reciben parámetros de la UI
- Llaman a casos de uso
- Transforman datos para la presentación
- Manejan errores específicos de la UI (como redirecciones o páginas 404)
Esto también va a ayudar a que vuestros componentes de React queden mucho más limpios!
Inyección de dependencias: uniendo todas las capas
Para unir todo, utilizamos un sistema de inyección de dependencias. En este caso yo estoy usando ioctopus.
Este sistema nos permite:
- Inyectar implementaciones concretas en tiempo de ejecución
- Cambiar implementaciones fácilmente (por ejemplo, para testing)
- Mantener un gráfico de dependencias claro
Integrando con Next.js App Router
Una parte interesante de nuestro proyecto es cómo integramos Clean Architecture con Next.js App Router y sus React Server Components:
Este componente:
- Obtiene el controlador del contenedor de inyección
- Ejecuta la llamada para obtener los datos
- Renderiza un componente de UI con los datos
Testing: la gran ventaja de Clean Architecture
La arquitectura facilita enormemente las pruebas:
Para testing, podemos reemplazar fácilmente las implementaciones reales con mocks:
Beneficios observados
-
Mantenibilidad: Cambiar una implementación (como pasar de REST a GraphQL) solo requiere modificar la capa de infraestructura, sin afectar a entidades o casos de uso.
-
Testabilidad: Podemos probar cada capa de forma aislada, con mocks para las dependencias.
-
Separación clara de responsabilidades: Cada desarrollador puede trabajar en una capa específica sin interferir con otros.
-
Adaptabilidad a cambios de framework: Si Next.js evoluciona o queremos migrar a otro framework, solo necesitamos modificar la capa de UI/controladores.
-
Dominio centralizado: Las reglas de negocio están claramente definidas en las entidades, lo que facilita su comprensión.
Desafíos y soluciones
-
Curva de aprendizaje: Para desarrolladores que no estén familiarizados con arquitecturas limpias puede llevar un tiempo adaptarse.
-
Más código inicial: Clean Architecture implica más ficheros y más código. Más boilerplate en general.
Conclusión
Implementar Clean Architecture en un proyecto frontend con Next.js requiere esfuerzo inicial, pero los beneficios a medio y largo plazo compensan con creces:
- Código mantenible y escalable
- Tests más sencillos
- Menor acoplamiento entre componentes
- Mayor claridad en la separación de responsabilidades
En este proyecto, esta arquitectura me ha permitido evolucionar la aplicación de forma controlada, añadir nuevas características sin romper las existentes y facilitar la futura incorporación de nuevos desarrolladores al equipo.
Si estás construyendo una aplicación frontend compleja o que prevés mantener durante mucho tiempo, te animo a considerar Clean Architecture como un patrón que te ayudará a mantener tu código organizado, testeable y adaptable a los cambios futuros.