Concevoir un système de notifications : architecture pour push, e-mail et SMS

Concevez un système de notifications push, e-mail et SMS : une API, préférences et heures calmes, dédoublonnage, files par canal, retries et suivi de livraison.

· Fondateur et développeur

Publié le

Concevoir un système de notifications : architecture pour push, e-mail et SMS

Les notifications commencent par une ligne de code : quand une commande est expédiée, envoyer un e-mail. Un an plus tard, la même entreprise envoie du push, des e-mails et des SMS depuis une douzaine de services, les utilisateurs reçoivent la même alerte trois fois, personne ne peut dire si un e-mail de réinitialisation de mot de passe a été livré, et des messages marketing réveillent les gens à 3 heures du matin.

Un système de notifications règle cela en plaçant un seul service entre le code qui décide qu'un événement a eu lieu et les fournisseurs qui livrent les messages. Cet article le conçoit : l'API, les contrôles exécutés avant tout envoi, la livraison par canal avec des retries, et le suivi. Le diagramme vers la fin montre l'ensemble du flux.

Exigences

  • Les services produit envoient une notification en un seul appel, sans savoir quels canaux l'utilisateur a activés.
  • Push (iOS et Android), e-mail et SMS, avec des modèles par langue.
  • Les utilisateurs contrôlent les canaux et les sujets, et les heures calmes sont respectées.
  • Le même événement ne produit jamais deux fois le même message.
  • Chaque message a un statut : envoyé, livré, rejeté, en échec.
  • Un fournisseur lent ne retarde jamais les autres canaux.

L'API : des événements, pas des messages

Les services produit ne devraient pas construire d'e-mails. Ils envoient un événement avec les faits et une clé d'idempotence :

POST /notifications
{
  "idempotencyKey": "order-8812-shipped",
  "userId": "u_7",
  "type": "order_shipped",
  "data": {"orderId": "8812", "carrier": "UPS", "eta": "2026-10-30"}
}

Le service de notifications gère tout le reste : quels canaux ce type utilise, quel modèle, quelle langue, et si l'utilisateur veut le recevoir. Quand une équipe veut changer le texte ou ajouter le SMS à un type de notification, rien ne change dans le service de commandes.

Avant tout envoi

Quatre contrôles s'exécutent dans l'API, dans cet ordre, avant que le travail n'atteigne un canal :

  1. Dédoublonner. Stockez la clé d'idempotence avec un TTL et ignorez les répétitions. Les retries dans les services en amont sont normaux et ne doivent pas atteindre les utilisateurs sous forme de doublons.
  2. Vérifier les préférences. Consultez les canaux de l'utilisateur, ses désabonnements par sujet et ses heures calmes. Les alertes de sécurité comme les réinitialisations de mot de passe passent généralement outre les désabonnements marketing ; le marketing, jamais. Écrivez cette règle noir sur blanc.
  3. Plafonner le volume. Limitez les messages par utilisateur, par canal et par heure. Un bug qui déclenche 500 événements ne doit pas devenir 500 notifications push.
  4. Rendre. Choisissez le modèle selon le type de notification, le canal et la langue de l'utilisateur, et remplissez-le avec les données de l'événement.

Les heures calmes demandent le fuseau horaire de l'utilisateur. Retenez le message et programmez-le pour la fin de la plage au lieu de l'abandonner, sauf s'il est urgent.

Une file et un pool de workers par canal

Après les contrôles, l'API écrit une tâche par canal dans la file de ce canal. Des files séparées sont la décision la plus importante de la conception : quand le fournisseur SMS ralentit, les tâches SMS s'accumulent dans leur propre file pendant que le push et l'e-mail continuent de circuler.

Les workers de chaque canal appellent les fournisseurs et gèrent leurs particularités :

  • Push. Les appareils iOS passent par l'Apple Push Notification service et Android par l'API FCM HTTP v1. Les jetons d'appareil se périment : APNs répond 410 pour un jeton qui n'est plus actif et FCM renvoie une erreur unregistered. Supprimez ces jetons, sinon vous continuez à payer de la latence pour des messages que personne ne reçoit.
  • E-mail. Envoyez via un fournisseur et remontez les rebonds et les plaintes. Si vous envoyez des e-mails en masse, ajoutez les en-têtes de désabonnement en un clic définis par la RFC 8058. Les consignes pour les expéditeurs de Gmail exigent le désabonnement en un clic pour les messages marketing et d'abonnement des expéditeurs qui envoient plus de 5 000 messages par jour.
  • SMS. Le canal le plus cher par message et le plus réglementé. Réservez-le aux codes et aux alertes urgentes, et vérifiez les règles de chaque pays où vous envoyez (cet article n'est pas un conseil juridique).

Les workers réessaient les échecs temporaires avec un backoff exponentiel et du jitter, puis déplacent une tâche dans une file de lettres mortes après un nombre fixe de tentatives. Les échecs définitifs, comme une adresse invalide, ne sont pas réessayés.

Suivre la livraison

Les fournisseurs signalent ce qui s'est passé par des webhooks ou des codes de réponse : accepté, livré, rejeté, ouvert. Écrivez chaque changement de statut dans un journal de livraison indexé par l'ID de notification. Le support peut alors répondre en quelques secondes à « l'utilisateur a-t-il reçu l'e-mail de réinitialisation ? », et vous pouvez mesurer les taux de livraison par canal et par fournisseur.

Les mises à jour de statut arrivent dans le désordre et parfois en double. Stockez-les comme des événements horodatés et déduisez-en l'état courant, au lieu d'écraser une colonne de statut.

Notifications dans l'application

Beaucoup de produits gardent aussi une boîte de réception dans l'application : la cloche avec un compteur. Traitez-la comme un quatrième canal qui écrit en base au lieu d'appeler un fournisseur. Stockez une ligne par utilisateur et par notification avec un indicateur de lecture, indexez-la par utilisateur et par date, et laissez l'application récupérer la dernière page et le nombre de non-lus. Marquez les éléments comme lus par lots, pour qu'un utilisateur qui ouvre sa boîte ne déclenche pas cinquante écritures.

Ordres de grandeur

Avec un million d'utilisateurs actifs par jour et cinq notifications par utilisateur et par jour, le système envoie 5 millions de notifications par jour, environ 58 par seconde en moyenne. Ce sont les pics qui comptent : une campagne ou une alerte d'incident peut envoyer un million de messages en quelques minutes. Dimensionnez les files pour la rafale, laissez les workers les vider au rythme que chaque fournisseur autorise, et attendez-vous à ce que les fournisseurs vous limitent avant que vos propres workers ne manquent de capacité.

Le diagramme d'architecture

Les services produit envoient des événements à une API de notifications, qui lit les préférences et répartit le travail entre une file push, une file e-mail et une file SMS. Chaque file alimente ses workers, qui appellent APNs et FCM ou un fournisseur. Les statuts vont au journal.

14 composants · 14 connexions

Ouvrir dans Archboard
Contenu du diagramme
  • 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
Un système de notifications avec une seule API devant trois canaux. L'API dédoublonne, vérifie les préférences et rend les modèles ; chaque canal a ensuite sa file, ses workers et son fournisseur, et les statuts de livraison reviennent dans un journal unique.

Comment le lire :

  • À gauche : les services produit envoient des événements avec une clé d'idempotence à l'API de notifications. L'API lit le stockage des préférences et exécute les contrôles listés dans le bloc Markdown.
  • Au milieu : chaque canal a sa propre file et son propre pool de workers, donc un fournisseur lent ne bloque que son canal.
  • À droite : les workers appellent APNs et FCM, le fournisseur e-mail et le fournisseur SMS. Les mises à jour de statut reviennent vers le journal de livraison en arêtes pointillées, car elles arrivent plus tard et de façon asynchrone.

Ce qu'il faut mettre sur le diagramme

  • Les contrôles et leur ordre. Dédoublonnage, préférences, plafonds et rendu sont la vraie logique du système.
  • Un couloir par canal. Si tous les canaux partagent une file sur votre diagramme, ils en partagent probablement une en production aussi.
  • Le comportement de retry et de lettres mortes. Notez le backoff et la limite de tentatives sur chaque worker.
  • Le chemin de retour des statuts. Sans lui, personne ne peut déboguer un message manquant.

Pour le côté files et consommateurs, voyez l'article sur l'architecture événementielle. Le diagramme d'architecture microservices montre où se place un service partagé comme celui-ci parmi les autres, et l'article sur la cartographie des données RGPD mérite une lecture avant de stocker des coordonnées et des journaux de livraison.

Le construire dans Archboard

  1. Connectez-vous avec Google ou GitHub et créez un diagramme.
  2. Ajoutez un Conteneur du pack Modèle C4 pour les services produit et un autre pour l'API de notifications, ainsi qu'une Base de données sous l'API pour les préférences.
  3. Ajoutez trois nœuds File d'attente du pack Données, un par canal, et un Conteneur pour les workers de chaque canal.
  4. Ajoutez trois nœuds Système externe du pack Modèle C4 pour les fournisseurs et une Base de données pour le journal de livraison.
  5. Dessinez les arêtes de statut en pointillé et ajoutez un bloc Markdown du pack Base avec les contrôles avant envoi.

L'exemple utilise 14 nœuds de diagramme et reste dans les limites de l'offre Free.

FAQ

Pourquoi ne pas laisser chaque service envoyer ses propres notifications ? Parce que préférences, heures calmes, dédoublonnage et suivi de livraison doivent alors être construits dans chaque service, et ils divergent. Un service de notifications unique les applique partout de la même manière.

Comment éviter les notifications en double ? Exigez une clé d'idempotence sur chaque requête et conservez-la un certain temps. Une clé répétée est acquittée puis ignorée.

Push, e-mail et SMS doivent-ils partager une file ? Non. Des files et des pools de workers séparés empêchent un fournisseur lent ou défaillant de retarder les autres canaux.

Comment gérer les heures calmes ? Stockez le fuseau horaire et la plage calme de chaque utilisateur, et programmez les messages non urgents pour la fin de la plage au lieu de les envoyer ou de les abandonner.

Comment savoir qu'une notification a été livrée ? Enregistrez les réponses et les webhooks des fournisseurs dans un journal de livraison indexé par l'ID de notification. « Livré » ne veut pas dire la même chose selon le canal, alors enregistrez les noms de statut du fournisseur.

Commencez gratuitement — un siège, sans carte bancaire.

Articles associés

← Retour au blog

Ouvrez un canevas en une minute

Connectez-vous avec Google ou GitHub. Gratuit pour un siège. Passez à un forfait supérieur quand l’équipe vous rejoint.