Dans les architectures distribuées modernes, un système de notification est bien plus qu'une simple fonction « envoyer un e-mail ». C'est un composant critique de l'engagement utilisateur, servant de pouls à votre application. Qu'il s'agisse d'une alerte transactionnelle, d'une campagne marketing ou d'un message de chat en temps réel, la fiabilité et la latence de votre pipeline de notification ont un impact direct sur la confiance des utilisateurs. Concevoir un système capable de traiter des millions de messages par seconde tout en maintenant une faible latence nécessite une approche réfléchie du découplage, de la priorisation et de la gestion des canaux.
L'architecture fondamentale : le découplage via les files de messages
Le principe fondamental d'un système de notification évolutif est le découplage. La logique applicative qui génère un événement (par exemple, « Commande expédiée ») ne doit jamais bloquer sur le mécanisme de livraison. Au lieu de cela, elle doit publier un événement vers un courtier de messages, tel qu'Apache Kafka, RabbitMQ ou Amazon SNS. Cela garantit que si le service e-mail tombe en panne, l'application principale reste fonctionnelle.
Considérez le fragment de pseudocode suivant illustrant comment un service applicatif publie un événement sans bloquer :
function onOrderShipped(orderId, trackingId) {
const event = {
type: "ORDER_SHIPPED",
payload: { orderId, trackingId },
timestamp: Date.now()
};
// Fire and forget to the message queue
messageBroker.publish("notification-events", event);
// Log for observability but do not await delivery completion
logger.info("Notification event queued", { orderId });
}
Livraison multi-canaux et priorisation
Les utilisateurs s'attendent à recevoir des notifications via plusieurs canaux : e-mail, SMS, notifications push et toasts dans l'application. Chaque canal a des exigences de latence et des structures de coûts différentes. Les notifications push et les messages dans l'application sont généralement en temps réel et peu coûteux, tandis que le SMS est coûteux et l'e-mail est peu coûteux mais avec une latence plus élevée.
Pour gérer cela, vous avez besoin d'un Routeur de Notification. Ce service consomme les événements de la file et applique une logique de routage basée sur les préférences de l'utilisateur et la gravité de l'événement. Les événements critiques (comme « Code d'authentification à deux facteurs ») doivent contourner les files standard et utiliser un canal direct de haute priorité. Les événements non urgents (comme « Nouveau suivi ») peuvent être groupés pour réduire les appels API aux fournisseurs tiers.
La mise en œuvre d'une structure de file priorisée est essentielle :
class NotificationQueue {
constructor() {
this.priorityMap = new Map(); // 1: Critical, 2: High, 3: Normal
}
enqueue(notification) {
const priority = this.calculatePriority(notification);
if (!this.priorityMap.has(priority)) {
this.priorityMap.set(priority, []);
}
this.priorityMap.get(priority).push(notification);
}
dequeue() {
// Always serve the highest priority (lowest number) first
for (let i = 1; i <= 3; i++) {
const queue = this.priorityMap.get(i);
if (queue && queue.length > 0) {
return queue.shift();
}
}
return null; // Queue empty
}
}
Gestion des pannes et idempotence
Dans les systèmes distribués, la panne est inévitable. Un service de notification push peut expirer, ou une passerelle SMS peut rejeter une requête. Votre système doit gérer les nouvelles tentatives de manière élégante sans dupliquer les messages. C'est là que l'idempotence intervient. Chaque notification doit avoir un ID unique. Avant l'envoi, le système vérifie un cache (comme Redis) pour s'assurer que la notification n'a pas déjà été traitée.
De plus, implémentez un backoff exponentiel pour les nouvelles tentatives. Si une API tierce échoue, attendez 1 seconde, puis 2, puis 4, avant de réessayer. Après un certain nombre d'échecs (par exemple, 5), déplacez la notification vers une « File de Lettres Mortes » (DLQ) pour inspection manuelle ou livraison de secours (par exemple, si le SMS échoue, essayez l'E-mail).
Conclusion
Construire un système de notification est un équilibre entre fiabilité, latence et coût. En découplant la génération d'événements de la livraison, en utilisant des files de priorité pour les actions critiques et en imposant l'idempotence, vous pouvez créer un système qui évolue sans effort. À mesure que votre base d'utilisateurs grandit, n'oubliez pas de surveiller les métriques des taux de livraison et de la latence, en vous assurant que votre pipeline de notification reste la colonne vertébrale fiable de votre expérience utilisateur.