← Integraciones externas

UD10 · Ampliar

Lo que debes recordar

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:

El protocolo de ingeniería para integraciones externas
  1. Utiliza siempre clientes modernos y fluidos: RestClient es el estándar síncrono oficial desde Spring Boot 3.2.
  2. Nunca reutilices contratos ajenos: aplica el patrón Capa Anticorrupción (ACL) aislando los DTOs del proveedor de tu modelo de dominio.
  3. Protege la deserialización con @JsonIgnoreProperties(ignoreUnknown = true) para que cambios ajenos no rompan tu aplicación.
  4. 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).
  5. 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.
  6. Optimiza el consumo con @Cacheable para ahorrar peticiones, evitar costes y reducir latencias de cientos de milisegundos a 1 ms.
  7. Sanitiza todo archivo entrante: almacena los binarios con UUIDs aleatorios fuera del classpath y guarda el nombre original solo en base de datos.
  8. Protege el servidor contra denegación de servicio acotando los tamaños máximos de subida (max-file-size y max-request-size).
  9. Desacopla efectos secundarios: emite correos y webhooks de forma asíncrona con @Async y @TransactionalEventListener(phase = AFTER_COMMIT).
  10. Protege la descarga de ficheros con la cabecera estándar Content-Disposition: attachment y 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

  1. ¿Qué transformaciones técnicas ocurren cuando el backend pasa de ser un servidor HTTP pasivo a un cliente HTTP saliente?
  2. ¿Por qué RestClient es la opción recomendada en Spring Boot 3.2+ para aplicaciones síncronas tradicionales?
  3. ¿Por qué la latencia de una petición saliente a Internet es órdenes de magnitud mayor que una consulta a PostgreSQL local?
  4. ¿En qué consiste el antipatrón de fuga de contratos externos (External Contract Bleeding)?
  5. ¿Qué tres componentes estructuran el patrón Capa Anticorrupción (ACL) en una integración REST?
  6. ¿Qué función cumple la anotación @JsonIgnoreProperties(ignoreUnknown = true) en un DTO externo?
  7. ¿Cómo transforma un adaptador los códigos numéricos del proveedor en reglas y lógica de dominio propias?
  8. ¿Cuáles son las dos falacias de la computación distribuida más peligrosas en el desarrollo backend?
  9. ¿Qué es el agotamiento de hilos (Thread Starvation) y cómo una llamada externa lenta puede tumbar un servidor Tomcat?
  10. ¿Cuál es la diferencia exacta entre Connect Timeout y Read Timeout en una factoría de conexiones HTTP?
  11. ¿Qué es la degradación elegante (Graceful Degradation) y cómo se implementa con bloques de captura en servicios de integración?
  12. ¿Por qué almacenar en caché una respuesta externa con @Cacheable beneficia tanto al rendimiento como a la resiliencia?
  13. ¿Por qué el transporte de ficheros binarios sobre HTTP exige el estándar multipart/form-data?
  14. ¿Cómo opera el ataque de salto de directorio (Path Traversal) y por qué renombrar ficheros con UUID en disco lo neutraliza?
  15. ¿Por qué nunca se deben guardar ficheros subidos por usuarios dentro de la carpeta static del proyecto?
  16. ¿Qué cabecera HTTP estándar fuerza la descarga de un fichero con su nombre original en el navegador?
  17. ¿Por qué es un error crítico ejecutar llamadas HTTP salientes dentro de un método anotado con @Transactional?
  18. ¿Qué problema resuelve el uso de Eventos de Dominio junto a @TransactionalEventListener(phase = AFTER_COMMIT)?
  19. ¿Cómo protege la anotación @Async el tiempo de respuesta del controlador frente a notificaciones externas lentas?
  20. ¿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 RestClient con 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) y Read 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.