Adaptadores de proveedores
Un adaptador de proveedor es una capa de integración aislada (anti-corruption layer, ACL) que traduce el contrato externo del proveedor (proveedor de juegos, pasarela de pagos, KYC/AML, puntuación de riesgo, notificaciones, etc.) al lenguaje interno del dominio y viceversa. Protege el dominio frente a API inestables, anomalías de red y cambios de esquemas y de políticas de seguridad.
Objetivos clave:1. Desacoplamiento: ningún payload externo «en bruto» llega al núcleo.
2. Fiabilidad: gestione los fallos (timeouts, reintentos, DLQ, circuit breaker).
3. Consistencia: idempotencia, orden por clave, mensajería transaccional.
4. Operación: métricas, trazas, límites, aislamiento por tenant y residencia de datos.
1) Responsabilidades del adaptador
Contratos: descripción de los esquemas/endpoints externos; mapeo → comandos/eventos internos.
Transporte: REST/gRPC/WebSocket/SQS/Kafka/SFTP; pool de conexiones, backpressure.
Seguridad: mTLS, OAuth2, HMAC, claves/certificados por tenant/región, rotación de secretos.
Fiabilidad: timeouts, reintentos con jitter, circuit breaker, deduplicación.
Idempotencia: `Idempotency-Key`/`request_id`, almacenamiento de respuestas/estados.
Observabilidad: métricas de SLO, logs estructurados, trazas.
Versionado: soporte de varias versiones de esquemas/endpoints.
Operaciones: feature flags, despliegues canary, sandboxes, certificación.
2) Dónde se usan (ejemplos de contextos)
Game/RGS: inicio/cierre de ronda, apuestas/ganancias, tokens de sesión, estados del proveedor.
Payments/PSP: depósitos/retiros, webhooks de estado, contracargos, 3-D Secure.
KYC/AML: verificaciones, controles de sanciones/PEP, monitoreo de transacciones.
Risk/Fraud: puntuación, disparadores, recomendaciones de bloqueo.
Comms: e-mail/SMS/push, límites de envío, plantillas.
Cada tipo tiene su propia máquina de estados de eventos y su SLA; el adaptador debe normalizarlos.
3) Contrato y mapeo (interno ↔ externo)
Principios:- Introducimos un Published Language dentro del adaptador y nunca dejamos pasar los campos del proveedor hacia el dominio.
- Todos los mensajes llevan `tenant_id`, `region`, `provider_id`, `operation_id`, `version_ts`.
- Admitimos varias versiones de los esquemas externos mediante mapeadores.
yaml mapping:
provider: "AcmeRGS"
version: "v3"
inbound:
SpinResultV3 -> Round. Resulted
BonusWinV3 -> Bonus. Wagered outbound:
StartRound -> POST /v3/sessions/{id}/start
Stake -> POST /v3/spins compat:
accepts: ["v2","v3"]
emits: ["v3"]
4) Idempotencia y orden
Deduplicación de solicitudes: `Idempotency-Key: <operation_id>` en las solicitudes; guardamos `(op_id → estado/respuesta final)` con TTL.
Deduplicación de webhooks: tabla `inbox(provider, event_id)` como clave primaria.
Orden por clave: serializamos las llamadas y el procesamiento por `aggregate_id` (por ejemplo, `round_id` o `psp_tx_id`).
Outbox/Inbox: mensajería transaccional en ambos extremos del flujo.
5) Fiabilidad: timeouts, reintentos, circuit breaker
Timeouts: cortos en el lado del cliente (orientados al p95), separados para connect/read.
Reintentos: solo en errores reintentables (5xx/timeout/429), backoff exponencial + full jitter, límite de intentos y plazo total.
Circuit Breaker: se abre cuando crecen los errores o la latencia; degradación controlada (por ejemplo, desactivar funciones secundarias del RGS o poner la operación en «esperando resultado»).
DLQ: mensajes «envenenados» con metadatos detallados y reprocesamiento seguro.
yaml reliability:
timeout_ms:
connect: 1000 read: 1500 retry:
max_attempts: 6 initial_backoff_ms: 200 max_backoff_ms: 8000 jitter: full retry_on: [TIMEOUT, 5xx, 429]
circuit_breaker:
failure_rate_threshold: 20% # por ventana slow_call_threshold_ms: 1500 half_open_max_calls: 10
6) Límites de tasa, cuotas, concurrencia
Respete los límites del proveedor (RPS, ráfagas, concurrencia).
Implemente WFQ/DRR por tenant (equidad) para que un cliente «ruidoso» no consuma todo el presupuesto.
Respete las cabeceras `Retry-After`/`X-RateLimit-`.
Colas internas + backpressure en el productor.
7) Seguridad y cumplimiento
Transporte: mTLS, TLS 1.2+, conjuntos de cifrado actualizados, pinning de certificados cuando sea posible.
Autenticación: OAuth2 client-credentials/mTLS, HMAC (hash firmado del cuerpo + timestamp), claves de API.
Minimización de datos personales: solo los campos necesarios; enmascaramiento/redacción en logs y DLQ.
Secretos: KMS/HashiCorp Vault, rotación automática, aislamiento por tenant/región.
Cumplimiento: PCI DSS para los PSP, almacenamiento de tokens en lugar del PAN, GDPR/leyes locales de datos.
8) Multi-tenant y multirregión
Configuración de claves/endpoints por tenant/región.
Residencia de datos: las llamadas se hacen desde la región «de origen»; entre regiones, solo datos agregados.
Aislamiento: pools de conexiones y límites propios por tenant.
yaml tenants:
T1:
region: eu-central provider_keys:
acme_rgs: { client_id: "...", cert_ref: "vault://..." }
psp_foo: { hmac_key_ref: "kms://..." }
endpoints:
acme_rgs: "https://eu. api. acme-rgs. com"
psp_foo: "https://eu. api. psp-foo. com"
T2:
region: sa-east
...
9) Observabilidad: métricas, logs, trazas
Métricas:- Éxitos/errores por clase (2xx/4xx/5xx/timeout/429).
- Latencia p50/p95/p99 por método.
- Activaciones del límite de tasa, apertura/cierre del breaker, tasa de DLQ, éxito del reprocesamiento.
- Logs estructurados: `tenant_id`, `provider_id`, `operation_id`, `endpoint`, `status`, `attempt`, `backoff_ms`.
- Trazas: un `trace_id` único, spans «serialize → send → receive → map → publish», etiquetas `schema_version`, `region`.
10) Versionado y feature flags
Mantenga en paralelo v1/v2 del contrato externo; la migración se hace de forma canary o por tenants.
Toda nueva función del proveedor va detrás de un flag; los cambios se activan sin release.
Acuerdo de evolución: primero cambios aditivos, validación estricta de esquemas (JSON Schema/Proto).
11) Runbooks
1. Avalancha de 429/límites: activar el throttling global, respetar `Retry-After`, redistribuir las ventanas entre tenants.
2. Aumento de timeouts: reducir la concurrencia, aumentar los timeouts con cautela, abrir el breaker, activar la degradación de funciones.
3. Schema mismatch: congelar el reprocesamiento, activar un mapeador compatible, ejecutar backfill/reprocesamiento.
4. Webhooks inestables: pasar al modo pull/reconcile, aplicar la deduplicación del inbox.
5. Incidente del proveedor: cambiar al sandbox/centro de datos de respaldo (si existe), activar las operaciones «diferidas».
12) Pruebas
Pruebas de contrato: productor/consumidor contra fixtures fijados del proveedor.
Pruebas de caos: retrasos, pérdidas, mensajes fuera de orden, duplicados, respuestas parciales, cortes de conexión.
Rendimiento: estrés con picos de ráfagas; medición de p95/p99, comportamiento del breaker.
Idempotencia: repetir el mismo `operation_id` no genera efectos adicionales.
E2E en sandboxes: escenarios happy path/contracargo/disputas/cancelación/recálculo.
13) Opciones de implementación (deployment patterns)
Servicio adaptador independiente: + aislamiento, releases independientes; − un salto de red adicional.
Sidecar/plugin: + llamadas locales; − versiones más difíciles de gestionar.
Biblioteca: + fácil de integrar; − alto acoplamiento y versiones heterogéneas.
Recomendación: un servicio adaptador con una API clara y su propio ciclo de releases.
14) Ejemplo de API del adaptador (pseudocódigo)
http
POST /adapters/psp/authorize
Headers:
X-Tenant: T1
Idempotency-Key: op-uuid
Body:
{ "amount":"10. 00","currency":"EUR","method":"card","card_token":"tok_..." }
→ 202 Accepted
{
"operation_id":"op-uuid",
"status":"PENDING",
"as_of":"2025-10-31T12:00:00Z"
}
Webhook del proveedor → adaptador → núcleo:
- Webhook con `provider_event_id` → `inbox` (clave primaria `(provider,event_id)`) → mapeo → evento de dominio `PaymentAuthorized`.
15) Errores típicos
Llevar el esquema externo «en bruto» al dominio → acoplamiento rígido y migraciones costosas.
Sin idempotencia ni inbox/outbox → efectos duplicados y estados fantasma.
Reintentos sin jitter ni límites → tormenta de solicitudes y bloqueo por límite de tasa.
Un único pool global sin equidad → un solo tenant «tumba» a todos.
Logs sin redacción de datos personales ni identificadores → imposible investigar incidentes y riesgo de cumplimiento.
Sin canaries ni flags → un release rompe a todos a la vez.
Ignorar `Retry-After` y los calendarios de mantenimiento del proveedor.
16) Checklist antes de producción
- Mapeo de los esquemas externos → lenguaje interno; versiones y compatibilidad hacia atrás.
- Idempotencia de solicitudes/webhooks (`operation_id`, `inbox`).
- Timeouts, reintentos con full jitter, circuit breaker, DLQ y reprocesamiento seguro.
- Límites de tasa y equidad por tenant; respeto de `Retry-After`.
- mTLS/OAuth/HMAC, rotación de secretos, minimización de datos personales, auditoría de accesos.
- Aislamiento regional y residencia de datos; configuración por tenant/región.
- Métricas p95/p99, errores por clase, tasa de breaker/429/DLQ; trazas.
- Sandboxes y pruebas de contrato; despliegue canary y feature flags.
- Runbooks de incidentes y formación del equipo de guardia.
- Documentación: SLA, límites, esquemas, procesos de evolución.
Conclusión
Los adaptadores de proveedores son el escudo y el traductor entre su dominio y el mundo exterior. Una ACL sólida, con idempotencia, control de errores y observabilidad, hace que las integraciones sean predecibles, reduce el coste de los cambios del proveedor y protege frente a los «fallos en cadena». Diseñe los adaptadores como componentes autónomos y gestionables, y el «mundo exterior» dejará de romper su arquitectura interna.