Caso de arquitectura · BBVA México

Notificaciones que ya no se pierden

Cómo pasé de envíos síncronos y de «mejor esfuerzo» a un sistema distribuido con patrón Outbox, colas de prioridad y self-healing.

en producción parcial México – España · revisiones asíncronas
El flujo en 16 segundos

Antes y ahora, paso a paso

Antes: cada módulo llama directo al proveedor y un 429 hace que el mensaje se pierda. Ahora: el mensaje se guarda, se despacha con control de flujo y se confirma como entregado. Activa el sonido: un tono positivo marca el camino correcto y uno negativo, el incorrecto.

8semanas de sprint, en paralelo a otro proyecto
+7 hde diferencia entre México y España
4colas de prioridad independientes
5piezas: de la fachada al self-healing

La necesidad

Que cada aviso llegue, o que se sepa por qué no

Varios módulos de negocio dependen de notificaciones por correo para avisar a los usuarios. Ese aviso tiene que llegar y, cuando no llega, el equipo necesita saber qué pasó con él en lugar de enterarse por una queja.

El problema

«A veces los correos simplemente no llegan»

Esa queja se repetía. Al revisar el sistema encontré la causa raíz: el envío era síncrono y de mejor esfuerzo. Si el proveedor respondía 429 (límite de cuota) o un error 5xx, la notificación se perdía sin dejar rastro y sin reintento.

Además, el código pertenecía a otro equipo y estaba poco documentado, así que proponer un cambio de fondo no era sencillo.

Antes

Síncrono

Cada módulo llamaba directo al proveedor. Un 429 significaba un mensaje perdido.

Ahora

Asíncrono y trazable

Cada mensaje se guarda antes de enviarse y termina entregado o registrado.

Cómo lo solucioné

Diagnóstico, propuesta y cinco piezas que se cuidan entre sí

Diagnóstico y propuesta

Documenté la causa raíz y pedí feedback a personas cuyo criterio admiro. Hubo un momento en que pensé en dejarlo en un parche; sus consejos me animaron a diseñar una propuesta completa y enviársela a mi líder, que la aprobó.

La arquitectura

  1. Eventos de negocio

    Los módulos llaman a una fachada única de envío, que filtra el entorno y prevalida dominios inválidos antes de tocar la red.

  2. Patrón Outbox

    Todo mensaje se persiste en PENDING antes de intentar enviarse. Si el proceso muere, no se pierde nada.

  3. Despacho HTTP y control de flujo

    El cliente sale hacia el API Gateway con rate limiter (token bucket) y exponential backoff a nivel transporte.

  4. Bifurcación inteligente

    Con 202 Accepted y correlation ID, el mensaje pasa a QUEUED y se cierra por webhook en SENT o BOUNCED. Los errores permanentes (422) van a dead letter sin reintento; los recuperables (429, 5xx) pasan a cuatro colas de prioridad con backoff exponencial: crítica, alta, normal y baja.

  5. Resiliencia y self-healing

    Un cron de reconciliación rescata mensajes huérfanos en PENDING o en QUEUED sin respuesta de webhook. El reproceso de dead letters reencola por lotes con un delay por índice (staggering) para evitar el thundering herd sobre el proveedor.

La ejecución

La implementación se coordinó con revisiones de código y arquitectura a distancia entre México y España, con pocas horas de horario en común, en un sprint de 8 semanas en paralelo a otro proyecto.

El resultado

Donde ya corre, no hay pérdidas silenciosas: cada mensaje termina entregado o registrado.

Hoy ya opera en parte de los módulos; la migración del resto sigue en curso.

El diagrama y la animación los hice con ayuda de Claude (IA); la arquitectura y el sistema, con mi equipo.

Conversemos en LinkedIn Volver al portafolio