Las notificaciones empiezan como una línea de código: cuando se envía un pedido, manda un email. Un año después, la misma empresa envía push, email y SMS desde una docena de servicios, los usuarios reciben la misma alerta tres veces, nadie sabe si se entregó un email de restablecimiento de contraseña y los mensajes de marketing despiertan a la gente a las 3 de la madrugada.
Un sistema de notificaciones lo arregla poniendo un único servicio entre el código que decide que algo ha pasado y los proveedores que entregan los mensajes. Este artículo lo diseña: la API, las comprobaciones que se ejecutan antes de enviar nada, la entrega por canal con reintentos y el seguimiento. El diagrama del final muestra todo el flujo.
Requisitos
- Los servicios de producto envían una notificación con una sola llamada, sin saber qué canales tiene activados el usuario.
- Push (iOS y Android), email y SMS, con plantillas por idioma.
- Los usuarios controlan canales y temas, y se respetan las horas de silencio.
- El mismo evento nunca produce dos veces el mismo mensaje.
- Cada mensaje tiene un estado: enviado, entregado, rebotado, fallido.
- Un proveedor lento nunca retrasa a los demás canales.
La API: eventos, no mensajes
Los servicios de producto no deberían construir emails. Envían un evento con los hechos y una clave de idempotencia:
POST /notifications
{
"idempotencyKey": "order-8812-shipped",
"userId": "u_7",
"type": "order_shipped",
"data": {"orderId": "8812", "carrier": "UPS", "eta": "2026-10-30"}
}
Todo lo demás es responsabilidad del servicio de notificaciones: qué canales usa este tipo, qué plantilla, qué idioma y si el usuario quiere recibirla. Cuando un equipo quiere cambiar el texto o añadir SMS a un tipo de notificación, no cambia nada en el servicio de pedidos.
Antes de enviar nada
En la API se ejecutan cuatro comprobaciones, en este orden, antes de que el trabajo llegue a un canal:
- Deduplicar. Guarda la clave de idempotencia con un TTL y descarta las repeticiones. Los reintentos en los servicios de origen son normales y no deberían llegar a los usuarios como duplicados.
- Comprobar preferencias. Consulta los canales del usuario, sus bajas por tema y sus horas de silencio. Las alertas de seguridad, como los restablecimientos de contraseña, suelen saltarse las bajas de marketing; el marketing nunca. Deja esa regla por escrito.
- Limitar el volumen. Limita los mensajes por usuario, canal y hora. Un error que dispara 500 eventos no debería convertirse en 500 notificaciones push.
- Renderizar. Elige la plantilla según el tipo de notificación, el canal y el idioma del usuario, y rellénala con los datos del evento.
Las horas de silencio necesitan la zona horaria del usuario. Retén el mensaje y prográmalo para el final de la franja en lugar de descartarlo, salvo que sea urgente.
Una cola y un pool de workers por canal
Tras las comprobaciones, la API escribe un trabajo por canal en la cola de ese canal. Las colas separadas son la decisión más importante del diseño: cuando el proveedor de SMS se ralentiza, los trabajos de SMS se acumulan en su propia cola mientras el push y el email siguen fluyendo.
Los workers de cada canal llaman a los proveedores y se ocupan de sus particularidades:
- Push. Los dispositivos iOS pasan por el Apple Push Notification service y los Android por la API HTTP v1 de FCM. Los tokens de dispositivo caducan: APNs responde 410 para un token que ya no está activo y FCM devuelve un error unregistered. Borra esos tokens o seguirás pagando latencia por mensajes que nadie recibe.
- Email. Envía a través de un proveedor e informa de los rebotes y las quejas. Si envías email masivo, añade las cabeceras de baja en un clic definidas en el RFC 8058. Las directrices para remitentes de Gmail exigen la baja en un clic en los mensajes de marketing y de suscripción de quienes envían más de 5000 mensajes al día.
- SMS. El canal más caro por mensaje y el más regulado. Resérvalo para códigos y alertas urgentes, y revisa las normas de cada país al que envías (este artículo no es asesoramiento legal).
Los workers reintentan los fallos temporales con backoff exponencial y jitter, y mueven un trabajo a una cola de mensajes fallidos tras un número fijo de intentos. Los fallos permanentes, como una dirección no válida, no se reintentan.
Seguimiento de las entregas
Los proveedores informan de lo ocurrido mediante webhooks o códigos de respuesta: aceptado, entregado, rebotado, abierto. Escribe cada cambio de estado en un registro de entregas indexado por el ID de la notificación. Así, soporte puede responder en segundos a «¿le llegó al usuario el email de restablecimiento?», y tú puedes medir las tasas de entrega por canal y proveedor.
Las actualizaciones de estado llegan desordenadas y a veces dos veces. Guárdalas como eventos con marca de tiempo y deriva de ellos el estado actual, en lugar de sobrescribir una columna de estado.
Notificaciones dentro de la app
Muchos productos también mantienen una bandeja dentro de la app: la campana con un contador. Trátala como un cuarto canal que escribe en una base de datos en lugar de llamar a un proveedor. Guarda una fila por usuario y notificación con un indicador de lectura, indexa por usuario y fecha, y deja que la app pida la página más reciente y el número de no leídas. Marca los elementos como leídos por lotes, para que un usuario que abre la bandeja no provoque cincuenta escrituras.
Cuentas rápidas
Con un millón de usuarios activos diarios y cinco notificaciones por usuario al día, el sistema envía 5 millones de notificaciones al día, unas 58 por segundo de media. Lo que importa son los picos: una campaña o una alerta de incidente puede enviar un millón de mensajes en pocos minutos. Dimensiona las colas para la ráfaga, deja que los workers las vacíen al ritmo que permita cada proveedor y cuenta con que los proveedores te limiten antes de que tus workers se queden sin capacidad.
El diagrama de arquitectura
Contenido del diagrama
- Product services
- Notification API
- Preferences
- Push queue
- Email queue
- SMS queue
- Push workers
- Email workers
- SMS workers
- APNs and FCM
- Email provider
- SMS provider
- Delivery log
- event
- opt-outs, quiet hours
- status
- webhooks
Cómo leerlo:
- Izquierda: los servicios de producto envían eventos con una clave de idempotencia a la API de notificaciones. La API lee el almacén de preferencias y ejecuta las comprobaciones listadas en el bloque Markdown.
- Centro: cada canal tiene su propia cola y su propio pool de workers, así que un proveedor lento solo atasca su canal.
- Derecha: los workers llaman a APNs y FCM, al proveedor de email y al proveedor de SMS. Las actualizaciones de estado vuelven al registro de entregas como aristas punteadas porque llegan más tarde y de forma asíncrona.
Qué poner en el diagrama
- Las comprobaciones y su orden. Deduplicación, preferencias, límites y renderizado son la lógica real del sistema.
- Un carril por canal. Si en tu diagrama todos los canales comparten una cola, probablemente también la compartan en producción.
- El comportamiento de reintentos y mensajes fallidos. Anota el backoff y el límite de intentos en cada worker.
- El camino de vuelta de los estados. Sin él, nadie puede depurar un mensaje perdido.
Para el lado de colas y consumidores, consulta el artículo sobre arquitectura orientada a eventos. El diagrama de arquitectura de microservicios muestra dónde se sitúa un servicio compartido como este entre los demás, y merece la pena leer el artículo sobre el mapeo de datos para el RGPD antes de guardar datos de contacto y registros de entrega.
Constrúyelo en Archboard
- Inicia sesión con Google o GitHub y crea un diagrama.
- Añade un Contenedor del pack Modelo C4 para los servicios de producto y otro para la API de notificaciones, y una Base de datos debajo de la API para las preferencias.
- Añade tres nodos Cola del pack Datos, uno por canal, y un Contenedor para los workers de cada canal.
- Añade tres nodos Sistema externo del pack Modelo C4 para los proveedores y una Base de datos para el registro de entregas.
- Dibuja punteadas las aristas de estado y añade un bloque Markdown del pack Básico con las comprobaciones previas al envío.
El ejemplo usa 14 nodos de diagrama, dentro del plan Free.
Preguntas frecuentes
¿Por qué no dejar que cada servicio envíe sus propias notificaciones? Porque entonces las preferencias, las horas de silencio, la deduplicación y el seguimiento de entregas tienen que construirse en cada servicio, y acaban divergiendo. Un único servicio de notificaciones las aplica igual en todas partes.
¿Cómo evito las notificaciones duplicadas? Exige una clave de idempotencia en cada petición y guárdala un tiempo. Una clave repetida se confirma y se descarta.
¿Deben compartir cola el push, el email y el SMS? No. Las colas y pools de workers separados evitan que un proveedor lento o con fallos retrase a los demás canales.
¿Cómo gestiono las horas de silencio? Guarda la zona horaria y la franja de silencio de cada usuario, y programa los mensajes no urgentes para el final de la franja en lugar de enviarlos o descartarlos.
¿Cómo sé que una notificación se entregó? Registra las respuestas y los webhooks de los proveedores en un registro de entregas indexado por el ID de la notificación. «Entregado» significa cosas distintas en cada canal, así que guarda los nombres de estado del propio proveedor.