Aplicaciones web y APIs
Hacen parte de la capa externa.
Errores a evitar:
- Poner lógica de negocio en la vista.
- Poner lógica de negocio en los controladores.
Integrar una aplicación web o un REST API dentro de una arquitectura limpia es más sencillo de lo que parece, siempre que recuerdes una regla básica: ambos pertenecen a la capa externa. Esta guía te muestra cómo conectar Spring Boot con tu dominio sin contaminar la lógica de negocio, ideal si trabajas con Java o cualquier framework web moderno.
¿Por qué la web y los APIs viven en la capa externa?
La interfaz que elijas (web, móvil, desktop o un API REST) es un detalle de entrega, no el corazón de tu sistema. Por eso, en Clean Architecture todas estas piezas se ubican en la capa más externa, lejos del dominio.
Esto tiene una consecuencia práctica muy poderosa: tu lógica de negocio sigue siendo agnóstica a la tecnología. Hoy expones tu sistema con Spring Boot, mañana podrías hacerlo con ASP.NET o con una app de escritorio, y el dominio no se entera.
¿Qué es la capa externa en Clean Architecture? Es la capa que contiene los detalles de tecnología: controladores web, frameworks, bases de datos y APIs. Todo lo que pueda cambiar sin romper las reglas de negocio.
¿Cómo se ve un controlador con Spring Boot dentro de Clean Architecture?
En el ejemplo en Java se agregó un paquete Rest dentro de la capa externa. Allí viven los controladores, como un HelloController, junto con la clase que levanta el servicio.
El controlador es deliberadamente simple. Define sus dependencias y las recibe vía inyección de dependencias, una funcionalidad que Spring Boot ofrece de forma automática. Una vez declaradas, esas dependencias se pueden usar en cualquier método del controlador sin instanciar nada manualmente.
Y aquí aparece algo interesante: este controlador sí tiene una implementación específica de Spring Boot. ¿Está mal? No, porque vive en la capa externa, justo donde los detalles tecnológicos son bienvenidos.
¿Cómo funciona la inyección de dependencias dinámica?
Para evitar configurar manualmente cada nuevo servicio, interfaz o repositorio, se implementó un código adicional que aprovecha funcionalidades de Spring Boot para descubrir dependencias en tiempo de ejecución e inyectarlas automáticamente.
Esto significa que cuando creas un nuevo caso de uso o repositorio, no necesitas tocar archivos de configuración. El sistema los encuentra solo. El código completo está disponible en el área de recursos.
La mayoría de los frameworks web ofrecen este mecanismo. ASP.NET, por ejemplo, trae inyección de dependencias prácticamente de base.
¿Qué errores debo evitar al construir REST APIs con arquitectura limpia?
Hay dos situaciones que se filtran constantemente en proyectos reales y que conviene cortar de raíz.
¿Por qué no debo poner lógica de negocio en la vista?
La tentación clásica es tomar un atajo: pongo la validación o la regla aquí en la vista, y después la muevo al dominio. Spoiler: ese después casi nunca llega. Los cambios temporales se vuelven permanentes y la lógica termina dispersa por toda la aplicación.
Si detectas lógica de negocio en una vista, tómate el tiempo de ubicarla bien:
¿Dónde debe vivir la lógica de negocio? Siempre en el dominio, nunca en la vista ni en el controlador. La capa externa solo orquesta y traduce.
¿Cómo deben comportarse los controladores?
El rol de un controlador es muy específico: recibir una solicitud desde la vista y decidir a qué parte del dominio invocar para resolverla. Nada más.
Un controlador se contamina cuando empieza a:
Lo ideal es que tus controladores funcionen como pasarelas. Llega información, haces validaciones generales propias de la vista (formato, campos requeridos), llamas al dominio, esperas un resultado y notificas de vuelta.
¿Qué validaciones puede hacer un controlador? Solo las que dependen del transporte: que el JSON tenga los campos esperados, que los tipos sean correctos, que el request esté bien formado. Las reglas de negocio no.
Mientras mantengas esa disciplina, integrar Spring Boot, ASP.NET o cualquier framework web con tu arquitectura limpia se vuelve un ejercicio mecánico. ¿Has tenido que rescatar lógica de negocio atrapada en un controlador? Cuéntame cómo lo resolviste.
Integrar una aplicación web o un REST API dentro de una arquitectura limpia es más sencillo de lo que parece, siempre que recuerdes una regla básica: ambos pertenecen a la capa externa. Esta guía te muestra cómo conectar Spring Boot con tu dominio sin contaminar la lógica de negocio, ideal si trabajas con Java o cualquier framework web moderno.
La interfaz que elijas (web, móvil, desktop o un API REST) es un detalle de entrega, no el corazón de tu sistema. Por eso, en Clean Architecture todas estas piezas se ubican en la capa más externa, lejos del dominio.
Esto tiene una consecuencia práctica muy poderosa: tu lógica de negocio sigue siendo agnóstica a la tecnología. Hoy expones tu sistema con Spring Boot, mañana podrías hacerlo con ASP.NET o con una app de escritorio, y el dominio no se entera.
¿Qué es la capa externa en Clean Architecture? Es la capa que contiene los detalles de tecnología: controladores web, frameworks, bases de datos y APIs. Todo lo que pueda cambiar sin romper las reglas de negocio.
En el ejemplo en Java se agregó un paquete Rest dentro de la capa externa. Allí viven los controladores, como un HelloController, junto con la clase que levanta el servicio.
El controlador es deliberadamente simple. Define sus dependencias y las recibe vía inyección de dependencias, una funcionalidad que Spring Boot ofrece de forma automática. Una vez declaradas, esas dependencias se pueden usar en cualquier método del controlador sin instanciar nada manualmente.
Y aquí aparece algo interesante: este controlador sí tiene una implementación específica de Spring Boot. ¿Está mal? No, porque vive en la capa externa, justo donde los detalles tecnológicos son bienvenidos.
Para evitar configurar manualmente cada nuevo servicio, interfaz o repositorio, se implementó un código adicional que aprovecha funcionalidades de Spring Boot para descubrir dependencias en tiempo de ejecución e inyectarlas automáticamente.
Esto significa que cuando creas un nuevo caso de uso o repositorio, no necesitas tocar archivos de configuración. El sistema los encuentra solo. El código completo está disponible en el área de recursos.
La mayoría de los frameworks web ofrecen este mecanismo. ASP.NET, por ejemplo, trae inyección de dependencias prácticamente de base.
Hay dos situaciones que se filtran constantemente en proyectos reales y que conviene cortar de raíz.
La tentación clásica es tomar un atajo: pongo la validación o la regla aquí en la vista, y después la muevo al dominio. Spoiler: ese después casi nunca llega. Los cambios temporales se vuelven permanentes y la lógica termina dispersa por toda la aplicación.
Si detectas lógica de negocio en una vista, tómate el tiempo de ubicarla bien:
¿Dónde debe vivir la lógica de negocio? Siempre en el dominio, nunca en la vista ni en el controlador. La capa externa solo orquesta y traduce.
El rol de un controlador es muy específico: recibir una solicitud desde la vista y decidir a qué parte del dominio invocar para resolverla. Nada más.
Un controlador se contamina cuando empieza a:
Lo ideal es que tus controladores funcionen como pasarelas. Llega información, haces validaciones generales propias de la vista (formato, campos requeridos), llamas al dominio, esperas un resultado y notificas de vuelta.
¿Qué validaciones puede hacer un controlador? Solo las que dependen del transporte: que el JSON tenga los campos esperados, que los tipos sean correctos, que el request esté bien formado. Las reglas de negocio no.
Mientras mantengas esa disciplina, integrar Spring Boot, ASP.NET o cualquier framework web con tu arquitectura limpia se vuelve un ejercicio mecánico. ¿Has tenido que rescatar lógica de negocio atrapada en un controlador? Cuéntame cómo lo resolviste.
Hacen parte de la capa externa.
Hola Manuel, muy chévere el curso, me gustaría preguntarte si cuando desde la capa externa como el controlador accede al servicio de aplicación podemos ir directamente a la clase de implementación o deberíamos seguir el estándar de ir contra la interfaz?
El mayor problema que tengo al implementar arquitecturas limpias en web o api rest es la complejidad del dominio y la simpleza de una petición. Usemos el ejemplo del curso del vuelo, la entidad tendría reservas, asientos, y supongamos que otros datos más que vienen de relaciones con otras entidades. Ahora, la petición de la api es obtener los vuelos disponibles, pero sin sus relaciones ya que sería muy pesado y no es necesario para ese endpoint aun. Cómo se debería manejar esta situación siguiendo los principios limpios?
@skatalites El archivo en cuestión se encuentra en el repositorio "manuelzapata/clean-architecture-example" y tiene la siguiente URL: https://github.com/manuelzapata/clean-architecture-example/blob/main/java/web-and-adapter/src/main/java/org/example/external/rest/Application.java.
El archivo "Application.java" se encuentra dentro de la carpeta "src/main/java/org/example/external/rest" del proyecto y representa la clase principal de la aplicación.
El código de la clase "Application" es el siguiente:
package org.example.external.rest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = "org.example")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
La clase "Application" está anotada con @SpringBootApplication, lo que indica que es una aplicación Spring Boot. Esta anotación combina varias anotaciones, como @Configuration, @EnableAutoConfiguration y @ComponentScan, y configura la aplicación para iniciar el contexto de Spring y habilitar la configuración automática de Spring Boot.
La clase contiene un método main() que es el punto de entrada de la aplicación. Utiliza SpringApplication.run() para iniciar la aplicación Spring Boot.
La anotación @SpringBootApplication también especifica scanBasePackages = "org.example", lo que indica a Spring que escanee y cargue los componentes de esa base de paquetes.
Este archivo "Application.java" es la clase principal de una aplicación Spring Boot que inicia el contexto de Spring y configura la aplicación para su ejecución.
Hola, al parecer estos recursos no fueron cargados al repo, por favor es posible validar si están otra ruta, gracias!
"En el domnio no debemos utilizar implementaciones específicas al framework". Aplica esto para anotaciones como Lombok por ejemplo?
Hola, Mauricio. Es una excelente pregunta.
La regla de oro es que el dominio debe ser agnóstico a cualquier framework. Si usas anotaciones de Lombok (como @Getter, @Setter o @Builder) en tus entidades de dominio, técnicamente estás introduciendo una dependencia externa.
Aunque Lombok es una herramienta de desarrollo y no un framework de ejecución, el principio de "independencia de frameworks" sugiere que tu dominio debería ser código Java puro (POJOs). Si en el futuro decides cambiar de herramienta o si el framework evoluciona, tu lógica de negocio no debería verse afectada ni obligada a cambiar.
Mi recomendación: Mantén el dominio limpio de dependencias externas. Si necesitas reducir el código repetitivo, considera si el beneficio de Lombok compensa el acoplamiento.
Para profundizar en cómo mantener estas capas aisladas, te recomiendo revisar: Desglose de Capas en Clean Architecture.
✅