El método
En esta unidad has aprendido a conectar tu backend con el mundo exterior sin comprometer su estabilidad, rendimiento ni seguridad.
Para diseñar e implementar cualquier integración externa profesional, aplica siempre este protocolo de 10 pasos:
- Utiliza siempre clientes modernos y fluidos:
RestClientes el estándar síncrono oficial desde Spring Boot 3.2. - Nunca reutilices contratos ajenos: aplica el patrón Capa Anticorrupción (ACL) aislando los DTOs del proveedor de tu modelo de dominio.
- Protege la deserialización con
@JsonIgnoreProperties(ignoreUnknown = true)para que cambios ajenos no rompan tu aplicación. - Asume las falacias de la red: toda llamada saliente debe tener Timeouts estrictos (Connect Timeout 2 s o menos, Read Timeout 3 s o menos).
- Aplica el principio de Degradación Elegante (Graceful Degradation): el fallo de una API externa opcional debe producir la respuesta degradada prevista por el contrato; un fallo de programación sigue requiriendo diagnóstico.
- Optimiza el consumo con
@Cacheablepara ahorrar peticiones, evitar costes y reducir latencias de cientos de milisegundos a 1 ms. - Sanitiza todo archivo entrante: almacena los binarios con UUIDs aleatorios fuera del classpath y guarda el nombre original solo en base de datos.
- Protege el servidor contra denegación de servicio acotando los tamaños máximos de subida (
max-file-sizeymax-request-size). - Desacopla efectos secundarios: emite correos y webhooks de forma asíncrona con
@Asyncy@TransactionalEventListener(phase = AFTER_COMMIT). - Protege la descarga de ficheros con la cabecera estándar
Content-Disposition: attachmenty reglas de autorización de Spring Security.
La idea más importante
Todo lo que ocurre fuera de tu servidor fallará tarde o temprano. Integrar con éxito una API o servicio externo no consiste en saber hacer una petición HTTP saliente, sino en diseñar tu aplicación para que siga funcionando cuando el proveedor externo se caiga, cambie su contrato o se quede congelado.
Un desarrollador principiante asume que la red es mágica y que los proveedores nunca fallan. Un ingeniero de software asume que la red se caerá en el peor momento posible y diseña barreras de contención (timeouts, adaptadores, cachés y degradación elegante) para que sus usuarios nunca sufran las consecuencias.
Las decisiones que tienes que saber justificar
| Decisión de ingeniería | Lo que tienes que poder defender ante un tribunal |
|---|---|
RestClient frente a RestTemplate y WebClient |
RestTemplate está en modo mantenimiento; WebClient exige arrastrar la reactividad de WebFlux; RestClient ofrece una interfaz fluida moderna y síncrona perfectamente integrada con Spring MVC. |
| Capa Anticorrupción (ACL) frente a devolver el JSON ajeno | Reenviar el JSON externo acopla el frontend y la base de datos a decisiones de terceros; el adaptador aísla el modelo y permite transformar códigos crudos en valor de negocio. |
@JsonIgnoreProperties(ignoreUnknown = true) |
Garantiza robustez y compatibilidad hacia adelante; si el proveedor añade 20 campos nuevos mañana, Jackson los ignora en silencio sin lanzar UnrecognizedPropertyException. |
| Timeouts obligatorios de conexión y lectura | Previene el colapso por agotamiento de hilos (Thread Starvation) en Tomcat; si un proveedor externo se congela, el hilo se libera en 2 segundos en lugar de quedarse bloqueado minutos. |
| Degradación Elegante (Graceful Degradation) | Si un servicio complementario (como el clima) falla, se devuelven los datos locales principales con un aviso por defecto en lugar de tumbar la experiencia del usuario con un error 500. |
Caché en memoria con @Cacheable |
Disminuye la latencia de 200 ms a 1 ms, ahorra ancho de banda, respeta los límites de tasa (rate limits) del proveedor y permite responder ante caídas temporales del servicio remoto. |
| Almacenar ficheros con UUID en disco | Neutraliza el ataque de salto de directorio (Path Traversal); el disco físico solo contiene identificadores seguros y la base de datos preserva el nombre original del usuario. |
| Almacenar ficheros fuera del directorio estático web | Impide la ejecución remota de código (RCE); un archivo malicioso .jsp o .sh no puede ser ejecutado directamente por el servidor web mediante una URL pública. |
@TransactionalEventListener(phase = AFTER_COMMIT) |
Garantiza que las notificaciones externas solo se emitan si la transacción local de base de datos se confirmó con éxito, evitando notificar acciones que sufrieron rollback. |
Ejecución asíncrona desacoplada con @Async |
Libera inmediatamente al hilo de Tomcat que atiende al usuario (respuesta en milisegundos), trasladando la espera de la red externa a un pool de hilos de fondo. |
Al terminar la unidad deberías poder responder
- ¿Qué transformaciones técnicas ocurren cuando el backend pasa de ser un servidor HTTP pasivo a un cliente HTTP saliente?
- ¿Por qué
RestClientes la opción recomendada en Spring Boot 3.2+ para aplicaciones síncronas tradicionales? - ¿Por qué la latencia de una petición saliente a Internet es órdenes de magnitud mayor que una consulta a PostgreSQL local?
- ¿En qué consiste el antipatrón de fuga de contratos externos (External Contract Bleeding)?
- ¿Qué tres componentes estructuran el patrón Capa Anticorrupción (ACL) en una integración REST?
- ¿Qué función cumple la anotación
@JsonIgnoreProperties(ignoreUnknown = true)en un DTO externo? - ¿Cómo transforma un adaptador los códigos numéricos del proveedor en reglas y lógica de dominio propias?
- ¿Cuáles son las dos falacias de la computación distribuida más peligrosas en el desarrollo backend?
- ¿Qué es el agotamiento de hilos (Thread Starvation) y cómo una llamada externa lenta puede tumbar un servidor Tomcat?
- ¿Cuál es la diferencia exacta entre Connect Timeout y Read Timeout en una factoría de conexiones HTTP?
- ¿Qué es la degradación elegante (Graceful Degradation) y cómo se implementa con bloques de captura en servicios de integración?
- ¿Por qué almacenar en caché una respuesta externa con
@Cacheablebeneficia tanto al rendimiento como a la resiliencia? - ¿Por qué el transporte de ficheros binarios sobre HTTP exige el estándar
multipart/form-data? - ¿Cómo opera el ataque de salto de directorio (Path Traversal) y por qué renombrar ficheros con UUID en disco lo neutraliza?
- ¿Por qué nunca se deben guardar ficheros subidos por usuarios dentro de la carpeta
staticdel proyecto? - ¿Qué cabecera HTTP estándar fuerza la descarga de un fichero con su nombre original en el navegador?
- ¿Por qué es un error crítico ejecutar llamadas HTTP salientes dentro de un método anotado con
@Transactional? - ¿Qué problema resuelve el uso de Eventos de Dominio junto a
@TransactionalEventListener(phase = AFTER_COMMIT)? - ¿Cómo protege la anotación
@Asyncel tiempo de respuesta del controlador frente a notificaciones externas lentas? - ¿Qué estrategia permite auditar las llamadas salientes a servicios de terceros para detectar degradaciones de rendimiento?
El vocabulario de la unidad
| Concepto | Significa |
|---|---|
| RestClient | Cliente HTTP síncrono, moderno y fluido introducido en Spring Boot 3.2 para realizar peticiones salientes con API declarativa. |
| Outbound HTTP | Petición HTTP iniciada por el propio servidor backend hacia un servicio o API remota en Internet. |
| Anticorruption Layer | Patrón arquitectónico (Capa Anticorrupción) que traduce y aísla los contratos externos ajenos del modelo de dominio interno. |
| External DTO | Objeto de transferencia que reproduce fielmente el formato de datos emitido por un proveedor externo. |
| Connect Timeout | Tiempo máximo que el cliente HTTP esperará para establecer la conexión TCP y la negociación TLS con el servidor remoto. |
| Read Timeout | Tiempo máximo de inactividad permitido entre paquetes de datos una vez que la conexión HTTP ya está abierta. |
| Thread Starvation | Agotamiento del pool de hilos de trabajo del servidor al quedar todos bloqueados esperando respuestas externas colgadas. |
| Graceful Degradation | Estrategia de diseño donde una aplicación sigue operativa con datos por defecto o funcionalidad reducida ante la caída de un servicio secundario. |
| Circuit Breaker | Patrón de estabilidad que interrumpe de inmediato las llamadas hacia un servicio externo averiado para proteger los recursos propios. |
| Multipart/form-data | Esquema de codificación HTTP (RFC 7578) para transmitir simultáneamente campos de texto y ficheros binarios divididos por límites. |
| Path Traversal | Vulnerabilidad de seguridad donde un atacante utiliza secuencias de salto (../) en el nombre de un archivo para escribir en directorios prohibidos. |
| Content-Disposition | Cabecera HTTP que indica si un recurso debe presentarse en el navegador (inline) o descargarse como fichero adjunto (attachment). |
| MIME Type | Identificador estándar en dos partes (ej: application/pdf) que describe la naturaleza y formato de un archivo transmitido por la red. |
| Webhook | Mecanismo de comunicación donde un servidor notifica a otro enviando una petición HTTP POST asíncrona ante un evento relevante. |
| Domain Event | Objeto inmutable que representa un hecho consumado de relevancia en el negocio dentro de la aplicación. |
| @TransactionalEventListener | Listener de Spring que sincroniza la ejecución de un manejador de eventos con una fase específica de la transacción (ej: tras el commit). |
| @Async | Anotación de Spring que desvía la ejecución de un método a un hilo secundario independiente gestionado por un ejecutor de tareas. |
Comprobación final del producto de la unidad
Integración externa y resiliencia · criterios de producción
- Las peticiones salientes utilizan el cliente moderno
RestClientcon URL base y cabeceras centralizadas. - Los contratos de proveedores externos están completamente aislados mediante el patrón Capa Anticorrupción (ACL).
- Los DTOs externos utilizan
@JsonIgnoreProperties(ignoreUnknown = true)para tolerar adiciones futuras de campos. - Toda llamada HTTP externa dispone de límites estrictos de
Connect Timeout(2 s o menos) yRead Timeout(3 s o menos). - La aplicación aplica degradación elegante ante caídas de red o errores 4xx/5xx sin colapsar con código 500.
- Las consultas a servicios externos con datos estables se optimizan mediante caché con
@Cacheable. - Los ficheros subidos se almacenan con UUIDs en un directorio externo al proyecto para prevenir ataques de *Path Traversal*.
- Los límites de tamaño para subidas (
max-file-size) y validación de tipos MIME están estrictamente configurados. - La descarga de ficheros está protegida por autorización y emite la cabecera estándar
Content-Disposition: attachment. - Los efectos secundarios (correos y webhooks) se ejecutan de forma asíncrona tras el commit (
@TransactionalEventListener).
Resultados de la unidad
- Consumir una API externa mediante un cliente HTTP.
- Aislar contratos externos con DTO propios.
- Tratar timeouts, errores y servicios no disponibles.
- Subir y descargar ficheros e integrar correo o webhooks.
Ya deberías ser capaz de
- Consumir una API externa mediante un cliente HTTP.
- Aislar contratos externos con DTO propios.
- Tratar timeouts, errores y servicios no disponibles.
- Subir y descargar ficheros e integrar correo o webhooks.