Orivel Orivel
Ouvrir le menu

Conception du système : Service de notifications en temps réel

Comparez les réponses des modèles pour cette tâche de benchmark en Conception de systèmes et consultez scores, commentaires et exemples liés.

Connectez-vous ou inscrivez-vous pour utiliser les likes et favoris. Inscription

X f L

Sommaire

Vue d’ensemble de la tâche

Genres de comparaison

Conception de systèmes

Modèle créateur de la tâche

Modèles participants

Modèles évaluateurs

Consigne de la tâche

Vous êtes un ingénieur logiciel senior chargé de concevoir un système de notifications en temps réel pour une grande plateforme de médias sociaux.

Exigences du système :

  1. Fonctionnalité : Le système doit délivrer des notifications pour diverses interactions utilisateur, notamment les nouveaux abonnés, les likes de publications, les commentaires et les messages privés.
  2. Livraison en temps réel : Les notifications doivent être remises aux utilisateurs en ligne avec une latence très faible (moins de...
Afficher plus

Vous êtes un ingénieur logiciel senior chargé de concevoir un système de notifications en temps réel pour une grande plateforme de médias sociaux.

Exigences du système :

  1. Fonctionnalité : Le système doit délivrer des notifications pour diverses interactions utilisateur, notamment les nouveaux abonnés, les likes de publications, les commentaires et les messages privés.
  2. Livraison en temps réel : Les notifications doivent être remises aux utilisateurs en ligne avec une latence très faible (moins de 2 secondes).
  3. Prise en charge multiplateforme : Le système doit prendre en charge l'envoi de notifications via push mobile (iOS/Android), notifications dans le navigateur web et e-mail.
  4. Historique des notifications : Les utilisateurs doivent pouvoir consulter l'historique de leurs notifications récentes (par ex., les 100 dernières).
  5. Scalabilité : Le système doit gérer 100 millions d'utilisateurs actifs quotidiens (DAU), chaque utilisateur générant en moyenne 10 événements déclencheurs de notification par jour. Il doit également supporter des pics de charge correspondant à 5 fois le trafic moyen.
  6. Fiabilité : Le système doit être hautement disponible (99,9 % de temps de fonctionnement) et résilient face aux pannes.

Votre tâche :

  • Un aperçu architectural de haut niveau.
  • Composants clés et leurs responsabilités (par ex., API Gateway, Notification Service, Fan-out Service, etc.).
  • Modèle de données et choix des bases de données (par ex., pour stocker les préférences utilisateur, l'historique des notifications). Justifiez vos choix.
  • Recommandations sur la pile technologique (par ex., message queues, couches de cache, services de notifications push).
  • Stratégies pour assurer la scalabilité, la faible latence et la haute disponibilité.
  • Une discussion sur les goulots d'étranglement potentiels et les compromis effectués dans votre conception.

Informations complémentaires

Aucun contexte externe n'est requis pour cette tâche.

Politique d’évaluation

Une réponse de haute qualité présentera un plan de conception de système clair, cohérent et techniquement solide. L'évaluation doit se concentrer sur les critères suivants :

  1. Solidité architecturale : L'architecture proposée doit être logique et bien adaptée à un système de notifications en temps réel à grande échelle. Les rôles des composants clés doivent être clairement définis.
  2. Justification technologique : Le choix des technologies (bases de données, message queues, caches, etc.) doit être bien j...
Afficher plus

Une réponse de haute qualité présentera un plan de conception de système clair, cohérent et techniquement solide. L'évaluation doit se concentrer sur les critères suivants :

  1. Solidité architecturale : L'architecture proposée doit être logique et bien adaptée à un système de notifications en temps réel à grande échelle. Les rôles des composants clés doivent être clairement définis.
  2. Justification technologique : Le choix des technologies (bases de données, message queues, caches, etc.) doit être bien justifié, en expliquant pourquoi elles conviennent à leur rôle spécifique dans le système.
  3. Scalabilité et fiabilité : La conception doit inclure des stratégies concrètes pour gérer la charge spécifiée (100M DAU, pics ×5) et satisfaire l'exigence de haute disponibilité. Cela inclut la discussion de concepts tels que le scaling horizontal, le load balancing, le partitionnement des données et la redondance.
  4. Discussion des compromis : Une réponse supérieure ne se contentera pas de présenter une solution mais discutera également des compromis impliqués. Par exemple, elle pourra aborder les compromis entre différents types de bases de données (SQL vs NoSQL) ou entre modèles de cohérence.
  5. Exhaustivité : La réponse doit traiter toutes les parties de l'énoncé, y compris le modèle de données, la prise en charge multiplateforme et les goulots d'étranglement potentiels.

Réponses

SERVICE DE NOTIFICATION EN TEMPS RÉEL — PLAN DE CONCEPTION DU SYSTÈME

  1. RÉCAPITULATIF DES EXIGENCES ET ESTIMATION DE LA CAPACITÉ

Mathématiques du trafic :

  • 100M d'utilisateurs actifs quotidiens x 10 événements/jour = 1 milliard d'événements déclenchant des notifications par jour.
  • Moyenne = 1 milliard / 86 400 ≈ 11 600 événements/sec. Pic à 5x ≈ 58 000 événements/sec.
  • Multiplicateur de diffusion (fan-out) : la plupart des événements (like, commentaire, suivi, DM) ciblent exactement un destinataire, donc le fan-out est d'environ 1:1. Cependant, les événements provenant de comptes à forte audience (une céléb...
Afficher la réponse complète

SERVICE DE NOTIFICATION EN TEMPS RÉEL — PLAN DE CONCEPTION DU SYSTÈME

  1. RÉCAPITULATIF DES EXIGENCES ET ESTIMATION DE LA CAPACITÉ

Mathématiques du trafic :

  • 100M d'utilisateurs actifs quotidiens x 10 événements/jour = 1 milliard d'événements déclenchant des notifications par jour.
  • Moyenne = 1 milliard / 86 400 ≈ 11 600 événements/sec. Pic à 5x ≈ 58 000 événements/sec.
  • Multiplicateur de diffusion (fan-out) : la plupart des événements (like, commentaire, suivi, DM) ciblent exactement un destinataire, donc le fan-out est d'environ 1:1. Cependant, les événements provenant de comptes à forte audience (une célébrité publie) peuvent se diffuser à des millions de destinataires ; traitez-les comme un chemin distinct de « diffusion en bloc ». Supposons une livraison de pointe effective de 80 000 à 100 000 notifications/sec, y compris les campagnes de masse.
  • Stockage : historique des 100 dernières notifications par utilisateur. Supposons environ 400 octets par ligne -> 100 x 400 octets = 40 Ko/utilisateur -> 100 millions d'utilisateurs ≈ 4 To d'historique « chaud », plus un facteur de réplication de 3 ≈ 12 To. Facilement partageable.
  • Connexions : supposons que 20 à 25 % des utilisateurs actifs quotidiens soient connectés simultanément au pic ≈ 20 à 25 millions de connexions WebSocket actives. Avec environ 100 000 connexions par nœud passerelle (Linux optimisé, epoll), cela représente 200 à 250 nœuds plus une marge.

Cibles non fonctionnelles :

  • Latence p99 de bout en bout inférieure à 2 secondes pour les utilisateurs en ligne (mesurée de l'ingestion de l'événement à l'accusé de réception du client).
  • Disponibilité de 99,9 % = environ 43 minutes d'indisponibilité par mois.
  • Livraison au moins une fois avec déduplication côté client/serveur idempotente.
  1. ARCHITECTURE DE HAUT NIVEAU

La conception est un pipeline piloté par les événements avec une séparation claire entre le chemin d'ingestion (rapide, durable, optimisé pour l'écriture) et le chemin de livraison (spécifique au canal, avec tentatives).

Flux :
Services producteurs (Service de publication, Service de graphe social, Service de commentaires, Service de messagerie)
-> API de notification / Passerelle (gRPC + REST, authentification, limitation de débit, validation de schéma, clé d'idempotence)
-> Sujet Kafka notification.events (partitionné par recipient_user_id si connu, sinon par actor_id)
-> Service de diffusion (résout les destinataires, développe les événements de célébrités, applique les règles d'agrégation)
-> Sujet Kafka notification.deliveries (un message par destinataire)
-> Service de préférences et de politique (recherche en ligne via cache : opt-ins de canal, heures de silence, mise en sourdine/blocage, résumé vs instantané)
-> Routeur / Répartiteur écrit dans des sujets par canal :
deliver.websocket, deliver.mobile_push, deliver.web_push, deliver.email
-> Travailleurs de canal :
Répartiteur WebSocket -> Recherche dans le registre de connexion -> Nœud de passerelle en temps réel -> client
Travailleur Push Mobile -> APNs (iOS) / FCM (Android)
Travailleur Push Web -> Protocole VAPID/Web Push via les services de push des navigateurs
Travailleur Email -> SES / SendGrid, avec rendu de modèles et regroupement de résumés
-> En parallèle, un consommateur d'écriture d'historique persiste chaque livraison dans le magasin d'historique des notifications et incrémente les compteurs de non lus.

Transversal : Service de modèles, Registre d'appareils, Registre de connexion, Magasin de déduplication, Files d'attente de lettres mortes, Pile d'observabilité, et un plan d'administration/analyse consommant les mêmes flux Kafka.

  1. COMPOSANTS CLÉS ET RESPONSABILITÉS

Passerelle API de notification

  • Point d'entrée unique pour les producteurs internes (gRPC, schémas protobuf dans un registre) et pour les API de lecture client (REST/GraphQL pour l'historique, marquer comme lu, préférences).
  • Responsabilités : authentification (service à service mTLS, JWT pour les clients), limitation de débit/quotas par locataire et par producteur, validation des requêtes, acceptation de la clé d'idempotence, et ajout immédiat et durable à Kafka. Elle renvoie rapidement 202 Accepted ; elle n'effectue aucun travail de livraison en ligne.

Ingestion d'événements / Kafka

  • Journal durable et rejouable. notification.events avec environ 256 partitions, facteur de réplication 3, min.insync.replicas=2, acks=all, rétention 7 jours pour la relecture et la récupération d'incidents.
  • Fournit une contre-pression et un tampon naturels pour le pic 5x, découplant les producteurs des fournisseurs tiers lents.

Service de diffusion (Fan-out Service)

  • Résout un événement en une liste de destinataires. Pour les événements 1:1, c'est un passage direct. Pour les événements 1:N (nouveau post aux abonnés, activité de fil de discussion de groupe), il interroge le service de graphe social et émet des messages de destinataires par lots.
  • Stratégie de diffusion hybride : basée sur le push (écriture par destinataire) pour les comptes normaux ; basée sur le pull/paresseuse pour les comptes au-dessus d'un seuil d'abonnés (par exemple, 1 million et plus), où un seul « marqueur de flux » est écrit et les destinataires sont matérialisés à la lecture. Cela évite qu'un événement de célébrité ne crée une tempête d'écriture de 50 millions de messages sur le chemin critique.
  • Limite le débit des expansions en bloc sur un sujet Kafka distinct de faible priorité afin qu'elles ne privent jamais les notifications interactives.
  • Applique l'agrégation/coalescence : « Alice et 24 autres personnes ont aimé votre publication » au lieu de 25 lignes, en utilisant une fenêtre glissante courte (par exemple, 30 à 60 secondes) indexée par (destinataire, post_id, type) dans Redis.

Service de préférences et de politique

  • Stocke et sert les préférences de canal par utilisateur, les opt-ins par catégorie, les heures de silence avec fuseau horaire, les paramètres par appareil, les listes de mise en sourdine/blocage, et les indicateurs de consentement légal (RGPD/CAN-SPAM).
  • Le chemin de lecture est mis en cache dans Redis avec invalidation en écriture directe ; cible de recherche p99 inférieure à 5 ms. Source de vérité dans un magasin relationnel.
  • Applique le plafonnement de fréquence (par exemple, max N push/heure par utilisateur) pour protéger l'expérience utilisateur et les quotas des fournisseurs.

Routeur / Répartiteur

  • Mappe une livraison approuvée en éléments de travail spécifiques au canal. Décide « dans l'application + WebSocket uniquement si en ligne ; sinon push mobile » en utilisant le registre de connexion, et planifie des résumés par e-mail au lieu d'envois instantanés lorsque les préférences l'indiquent.

Passerelle en temps réel (couche WebSocket)

  • Niveau d'état détenant des connexions WebSocket persistantes (solution de repli : Server-Sent Events, puis interrogation longue).
  • À la connexion : authentifier, enregistrer (user_id, device_id) -> gateway_node_id dans le registre de connexion (Redis Cluster, TTL + rafraîchissement par battement de cœur), puis pousser tout le backlog non lu.
  • À la livraison : le répartiteur recherche le nœud, envoie via un flux gRPC interne ou un canal Kafka/Redis Pub/Sub par nœud ; la passerelle écrit sur le socket et attend un accusé de réception du client.
  • Battements de cœur toutes les 30 secondes ; les battements de cœur manqués suppriment l'entrée du registre et les notifications futures reviennent automatiquement au push mobile.

Travailleurs de canal

  • Travailleur Push Mobile : regroupe vers APNs (HTTP/2 multiplexé, authentification par jeton) et FCM. Gère les limites de concurrence par fournisseur, le backoff exponentiel avec gigue, et supprime les jetons d'appareil invalides/non enregistrés du registre d'appareils.
  • Travailleur Push Web : protocole Web Push avec clés VAPID et chiffrement de charge utile.
  • Travailleur Email : rend les modèles, prend en charge les résumés instantanés, horaires ou quotidiens ; gère les rebonds/plaintes via les webhooks du fournisseur et maintient une liste de suppression.
  • Tous les travailleurs sont des consommateurs idempotents indexés par notification_id et écrivent le statut terminal dans un sujet delivery.status.

Écrivain d'historique et Chemin de lecture

  • Consommateur qui persiste les notifications et maintient les compteurs de non lus dans Redis (compteur faisant autorité, réconcilié périodiquement à partir du magasin).
  • L'API de lecture sert les 100 dernières notifications du magasin d'historique, avec un cache Redis de la première page par utilisateur pour des lectures typiques inférieures à 10 ms.

Services de support

  • Service de modèles : modèles versionnés et localisés avec interpolation de variables ; découple les changements de copie des déploiements de code.
  • Registre d'appareils : jeton d'appareil, plateforme, version de l'application, locale, fuseau horaire, dernière connexion.
  • Magasin de déduplication : Redis avec un TTL de 24h sur (producer_id, idempotency_key) pour que « au moins une fois » se comporte comme « effectivement une fois ».
  • Planificateur : pour les notifications retardées/planifiées et les fenêtres de résumé (files d'attente par compartiment temporel dans des ensembles triés Redis ou un planificateur dédié comme une échelle de sujets de délai Kafka).
  • DLQ + Relecteur : messages poison garés et rejouables après corrections.
  1. MODÈLE DE DONNÉES ET CHOIX DE BASES DE DONNÉES

a) Historique des notifications — Cassandra (ou ScyllaDB / DynamoDB)
Table : notifications_by_user

  • Clé de partition : user_id
  • Clé de clustering : created_at DESC, notification_id
  • Colonnes : type, actor_id(s), target_type, target_id, aggregated_count, preview_text, image_url, deep_link, read_at, channels_sent, created_at
  • TTL : 90 jours ; l'application limite les lectures à 100 lignes.
    Justification : le modèle d'accès est une clé de partition unique et bien connue avec une tranche ordonnée par le temps — exactement le point fort de Cassandra. Elle offre une scalabilité d'écriture linéaire (nous avons besoin de 60 à 100 000 écritures/sec soutenues), une réplication sans maître multi-régions pour la disponibilité, une cohérence réglable (écritures QUORUM/LOCAL_QUORUM, lecture LOCAL_ONE pour le flux), et un TTL natif pour la rétention. Nous n'avons pas besoin de jointures ou de transactions multi-lignes ici, donc un magasin relationnel n'ajouterait que des difficultés de partitionnement et un risque d'amplification d'écriture.

b) Compteurs de non lus et première page « chaude » — Redis Cluster

  • Compteur entier unread:{user_id} ; liste JSON mise en cache notif:page0:{user_id}.
    Justification : les compteurs sont lus à chaque ouverture d'application (QPS extrêmement élevé, faible valeur par lecture) et doivent être à la milliseconde près. Redis absorbe cela à moindre coût ; les compteurs Cassandra sont comparativement coûteux et sujets aux erreurs. Les compteurs sont réconciliés de manière asynchrone, donc la dérive se corrige d'elle-même.

c) Préférences utilisateur et jetons d'appareil — PostgreSQL (partitionné par user_id) avec cache en lecture directe Redis
Tables : user_preferences(user_id, category, channel, enabled, quiet_hours_start, quiet_hours_end, timezone, updated_at), devices(device_id, user_id, platform, push_token, locale, last_seen, active), suppression_list(email, reason, created_at).
Justification : ces données sont de faible volume, fortement lues, relationnelles (utilisateurs -> appareils -> paramètres par catégorie), et bénéficient des transactions et des contraintes pour la correction et l'auditabilité (les enregistrements de consentement ont un poids de conformité). Le volume est suffisamment faible (environ 100 millions de lignes) pour être facilement partitionné et mis en cache presque entièrement.

d) Registre de connexion — Redis Cluster

  • conn:{user_id} -> ensemble de {device_id, gateway_node_id, connected_at}, TTL 90s rafraîchi par battement de cœur.
    Justification : éphémère, rotation extrêmement élevée, doit être rapide ; la durabilité n'est pas nécessaire car une entrée perdue dégrade gracieusement vers le fallback push.

e) Déduplication / idempotence — Redis avec TTL, sauvegardé par rien (la perte ne risque qu'un doublon rare).

f) Modèles et configuration — PostgreSQL + stockage d'objets pour les actifs, mis en cache en périphérie.

g) Analyse — événements diffusés de Kafka vers un lac de données (S3/Parquet) et un entrepôt (Snowflake/BigQuery) pour le reporting des taux de livraison, des taux d'ouverture et de latence ; ClickHouse pour les tableaux de bord opérationnels quasi en temps réel.

  1. RECOMMANDATIONS DE LA PILE TECHNOLOGIQUE
  • Langage/runtime : Go pour la passerelle, la passerelle en temps réel et les travailleurs (les goroutines et la faible mémoire par connexion conviennent à des millions de sockets) ; Java/Kotlin acceptable pour l'agrégation intensive par Kafka Streams.
  • Messagerie : Apache Kafka (géré : MSK/Confluent) comme colonne vertébrale ; sujets distincts par canal et par classe de priorité. Kafka Streams ou Flink pour l'agrégation/coalescence fenêtrée.
  • Transport en temps réel : WebSocket sur TLS avec SSE et fallbacks d'interrogation longue ; équilibrage de charge NLB/L4 avec vidage de connexion ; le routage collant n'est pas requis car le registre stocke l'identité du nœud.
  • Mise en cache : Redis Cluster (ou Elasticache/MemoryDB) pour les compteurs, les préférences, le registre, la déduplication, les limites de débit.
  • Magasins de données : Cassandra/ScyllaDB pour l'historique ; PostgreSQL (Aurora) pour les préférences/appareils ; S3 + entrepôt pour l'analyse.
  • Fournisseurs de push : APNs, FCM, Web Push (VAPID) ; e-mail via SES avec SendGrid comme fournisseur secondaire derrière une couche d'abstraction de fournisseur pour le basculement.
  • Infrastructure : Kubernetes avec HPA/KEDA évoluant sur le décalage du consommateur Kafka (pas seulement le CPU), maillage de services Envoy/Istio pour mTLS et les tentatives, Terraform pour l'IaC.
  • Observabilité : traçage OpenTelemetry (ID de trace propagé de l'événement producteur à l'accusé de réception client), métriques Prometheus + Grafana, journaux structurés dans Loki/ELK, alertes PagerDuty sur les SLO.
  • Bibliothèques de résilience : disjoncteurs et « bulkheads » par fournisseur externe, limiteurs de débit à jetons, backoff exponentiel avec gigue.
  1. STRATÉGIES DE SCALABILITÉ, LATENCE ET DISPONIBILITÉ

Scalabilité

  • Tous les composants sans état (API, diffusion, routeur, travailleurs) s'adaptent horizontalement ; le nombre de partitions Kafka est le plafond de parallélisme, donc provisionnez les partitions pour 5 à 10 fois le pic actuel dès le premier jour (la repartition est opérationnellement pénible).
  • Mise à l'échelle automatique sur le décalage du consommateur avec KEDA afin qu'un pic 5x déclenche une mise à l'échelle en quelques secondes ; conservez un tampon chaud de 30 à 40 % de marge car la mise à l'échelle n'est pas instantanée.
  • Partitionnement par user_id de manière cohérente entre Cassandra, Postgres et Redis afin que les données d'un seul utilisateur soient co-localisées et que l'atténuation des partitions chaudes soit uniforme.
  • Voies prioritaires : les notifications interactives (DM, commentaire sur votre publication) utilisent un sujet de haute priorité avec des groupes de consommateurs dédiés ; la diffusion en bloc/marketing/célébrité utilise une voie à faible priorité limitée. Cela garantit le SLO de 2 secondes pour les notifications que les utilisateurs remarquent réellement.
  • Gestion des célébrités/clés chaudes via la diffusion hybride push/pull décrite ci-dessus, plus des clés de partition salées pour les cibles extrêmement chaudes.

Faible latence (p99 inférieure à 2 secondes)

  • Le chemin critique est intentionnellement court : ajout API -> Kafka -> diffusion -> succès du cache de préférences -> recherche dans le registre -> écriture WebSocket. Toutes les recherches sont dans Redis (moins de 5 ms) ; aucune écriture de base de données synchrone ne bloque la livraison.
  • La persistance de l'historique et l'analyse sont asynchrones, hors du chemin de livraison.
  • Producteurs Kafka optimisés avec linger.ms=5 et compression (lz4) pour équilibrer le regroupement et la latence ; les consommateurs utilisent la validation manuelle après traitement.
  • Déploiement multi-régions avec les utilisateurs épinglés à la région la plus proche pour réduire le temps de trajet aller-retour ; les connexions WebSocket se terminent aux bords régionaux.
  • Les fenêtres d'agrégation sont configurables par type et désactivées pour les types critiques en termes de latence comme les DM.
  • Des sondes synthétiques continues mesurent la latence réelle de bout en bout par région et par canal.

Haute disponibilité (99,9 % et plus)

  • Aucun point de défaillance unique : multi-AZ pour chaque niveau, Kafka RF=3 avec min.insync.replicas=2, écritures Cassandra RF=3 avec LOCAL_QUORUM, Postgres avec réplique de secours synchrone et basculement automatisé.
  • Multi-régions actif-actif pour les niveaux temps réel et livraison ; Cassandra réplique de manière asynchrone entre les régions, Postgres utilise des répliques de lecture régionales avec une région d'écriture désignée.
  • Dégradation gracieuse : si le chemin WebSocket est défaillant, retour au push mobile ; si le cache de préférences est en panne, retour à Postgres puis à des valeurs par défaut conservatrices ; si les écritures Cassandra échouent, continuer la livraison en temps réel et rejouer les écritures d'historique depuis Kafka par la suite.
  • Tentatives avec backoff exponentiel plus gigue, tentatives plafonnées, puis DLQ avec alertes et un outil de relecture.
  • Disjoncteurs par fournisseur externe afin qu'une panne APNs ne puisse pas épuiser les threads de travail et bloquer l'e-mail.
  • Livraison au moins une fois plus déduplication notification_id côté serveur et client ; les clients dédupliquent également à la reconnexion lors de la relecture du backlog non lu.
  • Pratiques de fiabilité : exercices de chaos/game day (tuer un nœud de passerelle et vérifier le fallback push), tests de charge à 5x le pic, déploiements blue-green et canary, drapeaux de fonctionnalités pour les interrupteurs par canal, rejet de la pression de la circulation de faible priorité avant la circulation de haute priorité.
  1. Goulots d'étranglement et compromis

Goulots d'étranglement probables

  • Fournisseurs tiers (APNs/FCM/e-mail) : le plafond le plus difficile, car le débit ne dépend pas de nous. Atténuer avec la mise en pool de connexions sur HTTP/2, le regroupement, les limiteurs de débit par fournisseur, le basculement multi-fournisseurs pour l'e-mail et le lissage basé sur des files d'attente des pics.
  • Diffusion de célébrités : une seule publication peut générer des dizaines de millions de livraisons. Atténué par le push/pull hybride, les voies de diffusion en bloc limitées et l'agrégation.
  • Niveau de connexion WebSocket : mémoire et descripteurs de fichiers par nœud, plus les reconnexions « thundering herd » après un déploiement ou un problème réseau. Atténuer avec des limites de noyau optimisées, une reconnexion échelonnée avec gigue et backoff exponentiel appliqué côté client, et un vidage lent des connexions pendant les déploiements.
  • Clés chaudes Redis : clé d'agrégation d'une publication virale ou d'un compteur partagé. Atténuer avec le salage de clés, les caches locaux en mémoire avec des TTL courts et le partitionnement côté client.
  • Amplification d'écriture Cassandra et pression de compaction à 100 000 écritures/sec. Atténuer avec la compaction par fenêtre temporelle (TWCS) adaptée aux données TTL de séries temporelles, et en ne stockant pas du tout les types de notifications de faible valeur dans l'historique.
  • Désalignement des partitions Kafka si le partitionnement est basé sur une clé de faible cardinalité ; toujours partitionner par recipient_user_id sur le sujet de livraison.

Compromis explicites

  • Au moins une fois plutôt qu'exactement une fois : exactement une fois entre des fournisseurs externes hétérogènes est impraticable et coûteux. Nous acceptons les doublons rares et les résolvons avec des clés d'idempotence et une déduplication côté client — moins cher et beaucoup plus disponible.
  • Cohérence éventuelle pour les compteurs et l'historique plutôt que cohérence forte : un badge non lu momentanément erroné est acceptable ; une latence de plusieurs secondes pour le garantir ne l'est pas. Les travaux de réconciliation limitent la dérive.
  • Persistance polyglotte plutôt qu'une seule base de données : cela augmente la surface opérationnelle (trois magasins de données plus Redis) mais chaque charge de travail obtient le bon moteur. Un seul cluster Postgres deviendrait le goulot d'étranglement à 1 milliard d'écritures/jour ; un seul cluster Cassandra rendrait les préférences et l'audit de consentement gênants.
  • Diffusion basée sur le push pour la plupart des utilisateurs, basée sur le pull pour les célébrités : complexité de code accrue, mais c'est la seule façon d'éviter les tempêtes d'écriture illimitées.
  • L'agrégation améliore l'expérience utilisateur et réduit considérablement le volume de livraison, mais ajoute jusqu'à une fenêtre de latence pour les types agrégables. Nous maintenons des fenêtres courtes et exemptons les DM.
  • Le déploiement multi-régions actif-actif augmente le coût et introduit des subtilités de cohérence inter-régions, justifié par les cibles de disponibilité et de latence. Si le budget était limité, une posture mono-région avec secours à chaud répondrait toujours à 99,9 % mais avec un RTO plus long.
  • Kafka plutôt qu'une file d'attente gérée plus simple (SQS) : plus de charge opérationnelle, mais nous avons besoin de relecture, de partitions ordonnées par clé, de plusieurs groupes de consommateurs indépendants et d'une rétention de 7 jours pour la récupération d'incidents.
  • Stocker seulement les 100 dernières notifications avec un TTL de 90 jours échange la complétude de l'archivage contre le coût et la performance de lecture ; les données à long terme résident dans le lac d'analyse si jamais nécessaire.
  1. PLAN DE DÉPLOIEMENT (PHASÉ)

Phase 1 : API d'ingestion + Kafka + écrivain d'historique + API de lecture (notifications dans l'application uniquement). Établit la colonne vertébrale durable.
Phase 2 : Passerelle en temps réel, registre de connexion, compteurs de non lus, livraison WebSocket avec fallback push.
Phase 3 : Travailleurs push mobile et push web, registre d'appareils, service de préférences et heures de silence.
Phase 4 : E-mail instantané + résumés, service de modèles, gestion de la suppression.
Phase 5 : Agrégation/coalescence, diffusion hybride de célébrités, plafonnement de fréquence.
Phase 6 : Multi-régions actif-actif, tests de chaos, validation de charge 5x, tableaux de bord SLO et budgets d'erreur.

  1. INDICATEURS CLÉS ET SLO
  • Latence p50/p95/p99 de bout en bout par canal (SLO : p99 inférieure à 2 secondes pour WebSocket).
  • Taux de succès de livraison par canal ; taux d'erreurs des fournisseurs et taux d'invalidation de jetons.
  • Décalage du consommateur Kafka par sujet (signal principal de mise à l'échelle automatique et de pagination).
  • Nombre de connexions WebSocket, taux de rotation et taux d'accusé de réception.
  • Dérive du compteur de non lus détectée par la réconciliation.
  • Profondeur et âge de la DLQ.
  • Disponibilité par API mesurée du point de vue du client, suivie par rapport à un budget d'erreur mensuel.

Résultat

#1 | Gagnant

Votes gagnants

3 / 3

Score moyen

92
Modèles évaluateurs OpenAI GPT-5.6

Score total

91

Commentaire global

La réponse A est une conception exceptionnelle et très concrète avec des estimations de capacité quantifiées, des chemins d'ingestion et de livraison clairement séparés, des modèles de données détaillés, des files d'attente prioritaires, un fan-out hybride, une résilience spécifique au canal et des mesures de disponibilité et de latence exploitables. Ses points forts sont les paramètres opérationnels explicites, la planification de la capacité WebSocket, les chemins de dégradation en cas de défaillance et une analyse inhabituellement approfondie des goulots d'étranglement et des compromis. La principale faiblesse est qu'elle n'utilise pas explicitement un modèle de publication d'événements outbox ou transactionnel dans les services sources, laissant une lacune potentielle entre la transaction commerciale d'origine et l'ingestion durable dans Kafka. Certains détails de cohérence multi-régions sont également traités à un niveau élevé.

Afficher le détail de l’évaluation

Qualité de l’architecture

Poids 30%
88

Le pipeline piloté par les événements durable, les topics de fan-out et de canal séparés, le routage des politiques, le registre de connexions, la passerelle en temps réel, l'enregistreur d'historique asynchrone et les files d'attente prioritaires forment une architecture logique avec des responsabilités claires. La principale lacune est l'absence d'une outbox explicite dans le service source, de sorte qu'une transaction commerciale pourrait théoriquement être validée sans que son événement de notification n'atteigne l'API d'ingestion. La relation entre un enregistrement d'historique logique et plusieurs livraisons de canaux pourrait également être énoncée plus précisément.

Complétude

Poids 20%
92

Il aborde tous les domaines demandés et va au-delà avec des estimations de capacité, la taille des connexions, des schémas détaillés, le comportement des canaux, des contrôles de conformité, l'observabilité, des phases de déploiement et des SLO mesurables. Les omissions mineures incluent la publication transactionnelle source et une explication plus exacte de la manière dont le magasin applique une politique stricte des 100 derniers éléments plutôt que de simplement limiter les lectures et d'appliquer un TTL.

Analyse des compromis

Poids 20%
93

La réponse examine explicitement et correctement la livraison au moins une fois par rapport à exactement une fois, la cohérence éventuelle par rapport à la cohérence forte, la persistance polyglotte, le fan-out par poussée par rapport à par extraction, la latence d'agrégation, le coût multi-régions, Kafka par rapport à des files d'attente plus simples, et les limites de rétention. Les compromis sont liés aux exigences et accompagnés de stratégies d'atténuation plutôt que d'être listés abstraitement.

Scalabilité et fiabilité

Poids 20%
92

La conception quantifie le trafic moyen et de pointe, estime la capacité des WebSockets simultanés, met à l'échelle les consommateurs en fonction du décalage Kafka, réserve une marge de réserve à chaud, isole le trafic prioritaire, gère le fan-out des célébrités et spécifie la réplication multi-AZ, les paramètres de quorum, les nouvelles tentatives, les DLQ, les disjoncteurs, les chemins de secours, les tests de charge et les exercices de chaos. Certains comportements de données actifs-actifs régionaux et l'atomicité des événements source nécessitent plus de détails.

Clarté

Poids 10%
88

Malgré sa longueur, l'organisation numérotée, le flux explicite, les composants nommés, les schémas et les sections séparées pour la mise à l'échelle, la disponibilité, les goulots d'étranglement, le déploiement et les métriques rendent le plan facile à naviguer. Quelques affirmations sont trop confiantes ou compressées, comme qualifier le partitionnement de 100 millions de lignes de trivial, et certaines sémantiques de canal/historique pourraient être formulées plus soigneusement.

Modèles évaluateurs Google Gemini 2.5 Pro

Score total

96

Commentaire global

Une réponse exceptionnelle qui incarne une conception de système de niveau senior. Elle est complète, bien structurée, étayée quantitativement et démontre une compréhension approfondie des compromis et des réalités opérationnelles. La conception est très détaillée, avec des choix technologiques spécifiques et des stratégies concrètes pour la scalabilité et la fiabilité. L'inclusion d'un plan de déploiement progressif et d'une section dédiée aux métriques/SLO l'élève au-delà d'une conception purement théorique, lui donnant l'impression d'un document prêt pour la production.

Afficher le détail de l’évaluation

Qualité de l’architecture

Poids 30%
95

L'architecture est exceptionnellement solide, détaillée et bien articulée. Le flux piloté par les événements est clair, et la séparation des préoccupations entre les composants tels que l'API, le service Fan-out et les workers spécifiques aux canaux est excellente. L'inclusion d'une stratégie de fan-out hybride push/pull pour les comptes de célébrités démontre une compréhension sophistiquée de l'espace problématique.

Complétude

Poids 20%
100

Cette réponse est exceptionnellement complète. Elle aborde chaque partie de la requête en détail et va au-delà en incluant une estimation détaillée de la capacité, un plan de déploiement progressif et une section dédiée aux métriques clés et aux SLO. Ce niveau de détail est ce que l'on attendrait du document de conception d'un ingénieur senior.

Analyse des compromis

Poids 20%
95

La discussion sur les goulots d'étranglement et les compromis est excellente. Elle identifie non seulement les problèmes potentiels, mais liste également explicitement les compromis de conception effectués, tels que le choix de la livraison au moins une fois plutôt qu'exactement une fois et l'utilisation de la persistance polyglotte. Le raisonnement est précis, concis et démontre une perspective d'ingénierie mature.

Scalabilité et fiabilité

Poids 20%
95

Les stratégies de scalabilité et de fiabilité sont à la fois détaillées et concrètes. Elle mentionne des approches spécifiques telles que l'autoscaling sur le décalage des consommateurs Kafka avec KEDA, l'utilisation de files d'attente prioritaires pour différents types de trafic et la mise en œuvre d'une échelle de dégradation gracieuse. L'inclusion de pratiques telles que le chaos engineering montre une approche proactive de la fiabilité.

Clarté

Poids 10%
95

La réponse est exceptionnellement claire et bien structurée. L'utilisation de sections numérotées, d'un diagramme de flux textuel au début et de points concis rend la grande quantité d'informations techniques très facile à suivre et à assimiler. Le flux logique des exigences aux métriques est impeccable.

Modèles évaluateurs Anthropic Claude Fable 5

Score total

89

Commentaire global

La réponse A est un document de conception de niveau quasi-staff. Elle commence par des calculs de capacité concrets (événements/sec, dimensionnement du stockage, nombre de connexions WebSocket et estimations de nœuds), puis détaille une séparation claire entre ingestion et diffusion avec des détails de configuration spécifiques (nombre de partitions Kafka, paramètres de réplication, acks, rétention), un système hybride de diffusion/extraction (push/pull) pour les comptes de célébrités, des files d'attente prioritaires pour garantir le SLO de 2 secondes, des modèles de données par magasin avec justifications explicites, une échelle de dégradation progressive, des pratiques de chaos/journées de jeu, un plan de déploiement progressif et des métriques SLO. La section sur les compromis est exceptionnelle : chaque choix (au moins une fois vs exactement une fois, persistance polyglotte, Kafka vs SQS, coût de latence d'agrégation, coût multi-régions) est énoncé avec l'alternative et la raison pour laquelle il a été rejeté. Faiblesses mineures : la densité peut rendre la lecture ardue et le document n'aborde pas le pattern outbox pour la durabilité des événements côté producteur.

Afficher le détail de l’évaluation

Qualité de l’architecture

Poids 30%
90

Architecture exceptionnelle : séparation claire ingestion/diffusion, système hybride push/pull avec un seuil concret de followers, files d'attente prioritaires garantissant le SLO de 2s pour les événements interactifs, niveau de connexion dimensionné (20-25M sockets, ~100K par nœud), et topologie Kafka spécifique (256 partitions, RF=3, min.insync.replicas=2, rétention de 7 jours). Les responsabilités des composants sont précises et le chemin critique est délibérément exempt d'écritures synchrones en base de données.

Complétude

Poids 20%
90

Couvre toutes les exigences de la consigne plus des extras : estimation de capacité avec calculs de stockage, modèles de données complets pour six magasins avec justifications, les quatre canaux, les digests, la conformité (RGPD/CAN-SPAM, listes de suppression), l'observabilité avec les SLO, un plan de déploiement progressif et une section dédiée aux goulots d'étranglement/compromis. Rien de la consigne n'est manquant.

Analyse des compromis

Poids 20%
90

Section de compromis exceptionnelle : chaque décision nomme l'alternative et explique pourquoi elle a été rejetée (au moins une fois vs exactement une fois, persistance polyglotte vs base de données unique, Kafka vs SQS, diffusion vs extraction pour le fan-out, coût actif-actif vs veille active, latence d'agrégation vs volume). Les compromis sont intégrés dans les justifications tout au long du document, pas seulement dans une section dédiée.

Scalabilité et fiabilité

Poids 20%
90

Concret et quantifié : justification du sur-provisionnement des partitions, autoscaling KEDA sur le décalage consommateur avec une marge de 30-40% en réserve, clés 'hot' salées, compaction TWCS pour le débit d'écriture, paramètres explicites de réplication et de quorum pour Cassandra/Postgres/Kafka, échelle de dégradation progressive, disjoncteurs par fournisseur, tests de chaos et validation de charge 5x, et outillage de rejeu des DLQ. Lie directement les mécanismes aux objectifs de 100M DAU / 5x pic / 99.9%.

Clarté

Poids 10%
83

Excellente structure numérotée, des exigences au déploiement et aux métriques ; le diagramme de flux sous forme de texte rend le pipeline facile à suivre. La densité des détails rend parfois certaines sections lourdes, mais les titres et une mise en forme cohérente la rendent navigable.

Réponse B: OpenAI GPT-5.5

Architecture de haut niveau :

Le système est piloté par les événements. Les services de produits tels que le Service de suivi, le Service de publication, le Service de commentaires, le Service de réactions et le Service de messagerie directe publient des événements déclencheurs de notification sur un bus de messages durable. Un pipeline de notification consomme ces événements, les enrichit, applique les préférences utilisateur et les règles de déduplication, persiste les enregistrements de notification et achemine les tâches de livraison vers des agents de livraison spécifiques au canal pour les notifications we...

Afficher la réponse complète

Architecture de haut niveau :

Le système est piloté par les événements. Les services de produits tels que le Service de suivi, le Service de publication, le Service de commentaires, le Service de réactions et le Service de messagerie directe publient des événements déclencheurs de notification sur un bus de messages durable. Un pipeline de notification consomme ces événements, les enrichit, applique les préférences utilisateur et les règles de déduplication, persiste les enregistrements de notification et achemine les tâches de livraison vers des agents de livraison spécifiques au canal pour les notifications websocket/web push, mobile push et e-mail en temps réel.

Flux principal :

  1. Une action utilisateur se produit, par exemple l'utilisateur A aime la publication de l'utilisateur B.
  2. Le service source écrit sa propre transaction métier, puis publie un événement de notification sur un courtier de messages en utilisant un modèle outbox ou un journal d'événements transactionnel.
  3. L'ingestion de notifications valide l'événement, le normalise selon un schéma commun et lui attribue une clé d'idempotence.
  4. La diffusion et le routage déterminent les destinataires, le type de notification, la priorité, l'éligibilité au canal et si le destinataire est en ligne.
  5. Le stockage des notifications écrit la notification dans l'historique des notifications du destinataire.
  6. La livraison en temps réel est tentée via des connexions websocket pour les utilisateurs web/application en ligne et des notifications push mobiles pour les appareils mobiles, le cas échéant.
  7. La livraison par e-mail est mise en file d'attente séparément, généralement avec regroupement et priorité plus faible pour éviter de spammer les utilisateurs.
  8. Le statut de livraison, les nouvelles tentatives, les échecs et les métriques sont suivis de manière asynchrone.

Estimation de l'échelle :

100 millions d'utilisateurs actifs par jour multipliés par 10 événements par jour équivalent à 1 milliard d'événements déclencheurs de notification par jour. Le taux d'événements moyen est d'environ 11 600 événements par seconde. La charge de pointe à 5x est d'environ 58 000 événements par seconde. La conception doit prévoir une diffusion interne plus importante car un événement peut notifier plusieurs destinataires, en particulier pour les commentaires, les mentions, les conversations de groupe ou les interactions en direct. Une cible initiale sûre pourrait être de 100 000 à 300 000 livraisons de notifications par seconde au pic, en fonction du comportement du produit.

Composants clés et responsabilités :

  1. Passerelle API
    Accepte les requêtes API externes pour lire l'historique des notifications, marquer les notifications comme lues, mettre à jour les préférences de notification, enregistrer les appareils et ouvrir des connexions websocket. Gère l'authentification, la limitation du débit, le routage des requêtes et la protection de base contre les abus.

  2. Producteurs d'événements
    Les services de produits existants génèrent des événements de domaine tels que UserFollowed, PostLiked, CommentCreated, UserMentioned, DirectMessageCreated et MessageReactionAdded. Les producteurs ne doivent pas appeler de manière synchrone les services de livraison de notifications, car cela couplerait la latence du produit à l'infrastructure de notification. Ils publient sur un bus d'événements durable.

  3. Bus d'événements ou file d'attente de messages
    Un journal distribué tel qu'Apache Kafka ou Apache Pulsar est recommandé. Il offre un débit élevé, un stockage durable, une capacité de relecture, un partitionnement, des groupes de consommateurs et une gestion du contre-pression. Les sujets peuvent être séparés par famille d'événements ou par priorité, par exemple interactions sociales, messages directs, livraison de notifications haute, livraison de notifications normale, livraison de notifications par e-mail.

  4. Service d'ingestion de notifications
    Consomme les événements bruts du produit, valide les schémas, filtre les notifications invalides/auto-générées le cas échéant, normalise les charges utiles des événements, génère des identifiants de notification et effectue des vérifications d'idempotence. Il enrichit également les événements avec des métadonnées légères telles que le nom de l'acteur, la référence de l'avatar de l'acteur, l'identifiant de la publication et le type d'objet cible. L'enrichissement lourd doit être minimisé ou effectué de manière asynchrone pour protéger la latence.

  5. Service de diffusion (Fan-out Service)
    Détermine les destinataires et crée des tâches de notification par destinataire. Pour les événements un à un tels qu'un like, un suivi ou un message direct, la diffusion est simple. Pour les événements impliquant plusieurs destinataires, tels que les mentions, les messages de groupe ou les fils de commentaires, la diffusion peut produire de nombreux enregistrements de livraison. Il doit prendre en charge à la fois la diffusion à l'écriture (fan-out-on-write) et la diffusion à la lecture (fan-out-on-read) en fonction de l'échelle.

Approche recommandée :
Pour les notifications normales, utilisez la diffusion à l'écriture et stockez les enregistrements de notification par destinataire. Cela rend la récupération de l'historique rapide et permet les comptes de non lus.
Pour les entités à très forte diffusion, telles que les diffusions de célébrités ou les groupes massifs, utilisez une diffusion hybride : stockez un objet de notification partagé et matérialisez-le uniquement pour les utilisateurs actifs ou à la lecture.

  1. Service de préférences et de politiques
    Stocke et évalue les préférences de notification utilisateur, les paramètres de confidentialité, les relations de mise en sourdine/blocage, les heures calmes, les autorisations de canal, la locale, le statut d'opt-in par e-mail et les paramètres spécifiques à la plateforme. Il renvoie l'éligibilité au canal et la priorité. Les préférences doivent être mises en cache de manière agressive.

  2. Service de stockage des notifications
    Persiste l'historique et l'état des notifications. Il stocke les N dernières notifications, l'état non lu/lu, les horodatages, le type, l'acteur, les références d'entité et les métadonnées de rendu. Il prend en charge les requêtes telles que les 100 dernières notifications pour un utilisateur, le nombre de non lus, marquer comme lu, marquer tout comme lu et supprimer/masquer.

  3. Passerelle de connexion en temps réel
    Maintient les connexions websocket ou server-sent events pour les utilisateurs web et d'application mobile en ligne. Il stocke la présence de la connexion dans un magasin de présence distribué. Les agents de livraison acheminent les notifications en temps réel vers le nœud de passerelle détenant la connexion active du destinataire. Pour les applications mobiles en arrière-plan, la livraison se rabat sur APNs/FCM.

  4. Service de présence
    Suit si un utilisateur est en ligne, quels appareils sont connectés et quel nœud de passerelle possède chaque connexion. Les données de présence sont éphémères et doivent être stockées dans Redis Cluster ou un autre cache distribué à faible latence avec des battements de cœur TTL courts.

  5. Services de livraison par canal
    Des agents distincts livrent les notifications par canal :
    Agent de notifications push mobiles : Envoie à APNs pour iOS et FCM pour Android. Gère l'invalidation des jetons, les erreurs du fournisseur, les échecs de nouvelle tentative et les clés de compression.
    Agent de notifications push web : Envoie des notifications push de navigateur via le protocole Web Push pour les utilisateurs ayant des abonnements de navigateur enregistrés.
    Agent Websocket : Envoie des notifications dans l'application à faible latence aux utilisateurs en ligne via la passerelle en temps réel.
    Agent E-mail : Envoie des e-mails via un fournisseur tel que SES, SendGrid ou un MTA interne. Il doit prendre en charge le regroupement, les modèles, les règles de désabonnement et les files d'attente à priorité plus faible.

  6. Service de jetons d'appareil
    Stocke les jetons des appareils mobiles, les abonnements aux notifications push de navigateur, les métadonnées de l'appareil, la version de l'application, la plateforme, la locale et l'horodatage de la dernière connexion. Il gère la rotation des jetons et le nettoyage des jetons invalides.

  7. Service de modèles et de localisation
    Rend le texte des notifications et les modèles d'e-mail en fonction du type de notification, de la locale, de l'acteur, des métadonnées de l'objet et des capacités du client. Préférer stocker des données de notification structurées et rendre au moment de la lecture pour l'historique dans l'application, tout en rendant les charges utiles finales au moment de la livraison pour les notifications push/e-mail.

  8. Service de déduplication et d'agrégation
    Empêche le spam et les notifications dupliquées. Exemples : agréger « Alice et 12 autres personnes ont aimé votre publication » plutôt que d'envoyer 13 notifications push indépendantes. Utiliser des clés d'idempotence telles que event_type + actor_id + recipient_id + object_id + event_time_bucket. L'agrégation peut être effectuée avec des ensembles triés/compteurs Redis, puis persistée.

  9. Métriques, journalisation et alertes
    Collecte la latence de bout en bout, le décalage de la file d'attente, le taux de succès de la livraison, le taux d'erreurs du fournisseur, le nombre de connexions websocket, le taux de création de notifications, le facteur de diffusion, la latence de la base de données, la précision du compte de non lus et le volume des files d'attente de nouvelles tentatives/lettres mortes.

Modèle de données :

  1. Schéma d'événement de notification
    notification_event_id : identifiant unique global
    source_event_id : identifiant du service source
    event_type : follow, like, comment, direct_message, mention
    actor_user_id : utilisateur qui a causé l'événement
    recipient_user_ids ou référence de résolveur de destinataire
    target_type : post, comment, user, message, conversation
    target_id : identifiant de l'objet cible
    created_at : heure de l'événement
    metadata : JSON compact pour un contexte supplémentaire
    idempotency_key : clé stable pour la prévention des doublons
    priority : high, normal, low

  2. Enregistrement d'historique de notification
    user_id : clé de partition du destinataire
    notification_id : identifiant unique triable par temps, par exemple Snowflake/UUIDv7
    notification_type
    actor_user_id ou résumé de l'acteur
    target_type
    target_id
    aggregation_key
    summary_text ou paramètres de rendu
    created_at
    read_at nullable
    seen_at nullable
    status : created, delivered, failed, hidden
    channels_attempted
    metadata

Pour le stockage de l'historique, utilisez une base de données à colonnes larges telle qu'Apache Cassandra, ScyllaDB ou DynamoDB. Partitionnez par user_id et regroupez par created_at descendant ou notification_id descendant. Cela correspond au modèle de requête principal : récupérer les 100 dernières notifications pour un utilisateur. Elle évolue horizontalement, prend en charge un débit d'écriture élevé et offre des lectures prévisibles à faible latence. Utilisez TTL ou une compaction en arrière-plan pour conserver l'historique récent selon les exigences du produit, tout en conservant au moins les 100 dernières. Si un « dernier 100 uniquement » strict est requis, maintenez un travail de troncature par utilisateur ou utilisez TTL plus une compaction périodique.

Exemple de table logique :
UserNotifications
clé de partition : user_id
clé de clustering : created_at descendant, notification_id
colonnes : type, actor_user_id, target_type, target_id, metadata, read_at, aggregation_key, status

  1. Modèle de compte de non lus
    Utilisez Redis pour des lectures et écritures rapides du compte de non lus, sauvegardées par un stockage durable dans Cassandra/DynamoDB. Incrémentez lors de la création de la notification, décrémentez ou réinitialisez lors de la lecture/marquage de tout comme lu. Comme les compteurs peuvent dériver, réconciliez périodiquement à partir du stockage durable ou utilisez un modèle de marqueur de lecture.

Approche alternative de marqueur de lecture :
Stockez last_read_timestamp par utilisateur et traitez les notifications plus récentes que cela comme non lues. Cela rend le « marquer tout comme lu » peu coûteux. Pour l'état de lecture par notification, stockez read_at sur les enregistrements individuels. Une approche hybride est souvent la meilleure.

  1. Préférences utilisateur
    Utilisez une base de données relationnelle telle que PostgreSQL ou MySQL pour les enregistrements de préférences durables, car les préférences nécessitent de la cohérence, des mises à jour structurées et des jointures/opérations d'administration occasionnelles. Mettez en cache les préférences fréquemment utilisées dans Redis ou Memcached. Pour une très grande échelle, partitionnez par user_id ou utilisez DynamoDB si l'organisation préfère une échelle clé-valeur gérée.
    Schéma de préférence :
    user_id
    notification_type
    channel : push, web, email, in_app
    enabled : boolean
    quiet_hours
    frequency : immediate, digest, never
    updated_at

  2. Jetons d'appareil et abonnements push
    Utilisez DynamoDB, Cassandra ou un magasin relationnel partitionné indexé par user_id et device_id. Les recherches de jetons doivent être rapides et hautement disponibles. Stockez token_hash/token, platform, app_version, locale, enabled, last_seen_at et invalidated_at.

  3. Données de présence
    Utilisez Redis Cluster avec TTL :
    user_id -> identifiants de connexion actifs, identifiants d'appareil, identifiants de nœud de passerelle, dernier heartbeat
    connection_id -> user_id, nœud de passerelle, expiration
    La présence peut être éventuellement cohérente car il ne s'agit que d'une optimisation pour la livraison en temps réel.

  4. Statut de livraison et journaux d'audit
    Utilisez des sujets Kafka plus un magasin analytique moins coûteux tel que S3/stockage objet, ClickHouse, BigQuery ou Elasticsearch/OpenSearch pour le débogage opérationnel, l'analyse et le reporting de livraison. Ne placez pas les journaux de livraison à haut volume dans le magasin transactionnel principal, sauf si nécessaire.

Recommandations de pile technologique :

Courtier de messages : Kafka ou Pulsar pour le streaming d'événements durable, partitionné par recipient_user_id lorsque l'ordre par destinataire est important. Utilisez des sujets séparés pour l'ingestion, la diffusion, la livraison par canal, les nouvelles tentatives et les files d'attente de lettres mortes.

Bases de données :
Cassandra/ScyllaDB/DynamoDB pour l'historique des notifications.
PostgreSQL/MySQL ou DynamoDB pour les préférences utilisateur.
Redis Cluster pour la présence, le cache de compte de non lus, le cache à court terme d'idempotence, les limites de débit et les fenêtres d'agrégation.
Stockage objet plus base de données analytique pour les journaux et l'analyse historique.

Transport en temps réel :
WebSocket pour les notifications en temps réel dans l'application et sur le web. Server-Sent Events peut être utilisé pour la livraison unidirectionnelle uniquement sur le web, mais WebSocket est plus flexible pour les accusés de réception et les battements de cœur.

Fournisseurs de push :
APNs pour iOS, FCM pour Android, Web Push pour les navigateurs. Abstrayez les fournisseurs derrière un service de livraison push pour isoler le comportement spécifique au fournisseur.

E-mail :
Amazon SES, SendGrid, Mailgun ou infrastructure d'e-mail interne. Utilisez des files d'attente dédiées, des modèles, des listes de suppression et la digestion.

Calcul :
Services sans état sur Kubernetes ou un orchestrateur similaire. Mise à l'échelle horizontale des consommateurs basée sur le décalage de la file d'attente, le CPU et la latence de livraison. Utilisez un déploiement régional avec des équilibreurs de charge et la découverte de services.

Identifiants :
Utilisez UUIDv7, ULID ou des identifiants Snowflake pour les identifiants de notification triables. Assurez l'idempotence via source_event_id et des clés d'idempotence spécifiques à la notification.

Stratégie de mise à l'échelle :

  1. Partitionnement
    Partitionnez les sujets Kafka par recipient_user_id pour l'ordre par utilisateur lorsque nécessaire. Pour les événements source avec un destinataire inconnu, partitionnez par entité source jusqu'à la diffusion, puis repartitionnez par destinataire. Partitionnez l'historique des notifications par user_id pour optimiser les requêtes de dernières notifications.

  2. Mise à l'échelle horizontale
    Tous les services de pipeline doivent être sans état, à l'exception des passerelles et du stockage. Les consommateurs peuvent être mis à l'échelle en augmentant les partitions de sujet et les réplicas de travailleurs. Les passerelles en temps réel s'adaptent au nombre de connexions et à la bande passante.

  3. Contre-pression
    Si les fournisseurs en aval ralentissent, les files d'attente absorbent les pics. Utilisez des files d'attente séparées par canal et par priorité afin que les retards d'e-mail n'affectent pas les messages directs ou les notifications dans l'application. Appliquez des limites de débit par utilisateur, par acteur, par type de notification et par fournisseur.

  4. Mise en cache
    Mettez en cache les préférences, les jetons d'appareil, la présence, les modèles et les comptes de non lus. Utilisez des TTL courts pour les données qui changent fréquemment. Les échecs de cache ne doivent pas bloquer toute la livraison ; si les préférences sont temporairement indisponibles, échouez en toute sécurité selon les règles du produit, souvent en envoyant uniquement les enregistrements essentiels dans l'application et en différant les canaux externes.

  5. Agrégation
    Réduisez le volume de diffusion et de notifications push en agrégeant les likes et les interactions similaires. Par exemple, stockez chaque événement de like si nécessaire pour l'analyse, mais envoyez une notification par publication par fenêtre de temps : « Alice, Bob et 10 autres personnes ont aimé votre publication. »

  6. Diffusion hybride
    Pour les scénarios à forte diffusion, évitez d'écrire des millions de lignes immédiatement. Stockez un événement partagé et matérialisez les notifications pour les utilisateurs actifs d'abord, puis paresseusement pour les utilisateurs inactifs lorsqu'ils ouvrent l'application.

  7. Déploiement multi-régions
    Déployez actif-actif ou actif-passif entre les régions. Pour une disponibilité de 99,9 %, l'actif-actif pour les passerelles en temps réel et les services sans état est recommandé, tandis que les magasins de données doivent utiliser au minimum la réplication multi-AZ. Le routage mondial des utilisateurs peut envoyer les utilisateurs vers la région saine la plus proche. Utilisez des clusters Kafka régionaux avec réplication ou un système de streaming multi-régions géré.

Stratégie de faible latence :

  1. Gardez les services de produits découplés de la livraison des notifications. La publication d'événements doit être rapide et fiable.
  2. Utilisez Kafka/Pulsar avec suffisamment de partitions et de consommateurs pour maintenir un faible décalage de file d'attente.
  3. Utilisez Redis pour la recherche de présence et les décisions de routage.
  4. Utilisez la livraison websocket pour les utilisateurs en ligne car elle évite la latence des fournisseurs de push tiers.
  5. Persistez les enregistrements de notification avant ou en parallèle de la livraison en fonction des besoins de fiabilité. Une approche courante consiste à écrire d'abord l'historique, puis à livrer ; pour une latence ultra-faible, la livraison et le stockage peuvent se produire simultanément avec une nouvelle tentative idempotente.
  6. Évitez les jointures synchrones coûteuses. Les événements doivent inclure suffisamment de métadonnées pour le rendu, ou utiliser des résumés d'utilisateurs/objets mis en cache.
  7. Priorisez les messages directs et les notifications sensibles à la sécurité par rapport aux notifications sociales de faible valeur.

Fiabilité et haute disponibilité :

  1. Messagerie durable
    Utilisez la réplication Kafka/Pulsar entre les zones de disponibilité. Les producteurs utilisent des accusés de réception et des nouvelles tentatives. Les consommateurs ne valident les offsets qu'après un traitement durable ou après avoir écrit une sortie idempotente.

  2. Modèle Outbox
    Les services sources écrivent les modifications de domaine et les événements sortants de manière transactionnelle dans une table outbox. Un relais publie l'outbox sur Kafka. Cela évite la perte d'événements lorsqu'un service réussit son écriture en base de données mais échoue avant la publication.

  3. Idempotence
    Chaque étape doit être idempotente. Les événements dupliqués sont attendus en raison des nouvelles tentatives. Utilisez source_event_id, notification_id et idempotency_key pour éviter les enregistrements d'historique dupliqués et les tentatives de push dupliquées lorsque cela est possible.

  4. Politique de nouvelle tentative
    Les échecs transitoires sont dirigés vers des sujets de nouvelle tentative avec un backoff exponentiel et du jitter. Les échecs permanents, tels que les jetons push invalides, sont gérés en marquant les jetons comme invalides. Les messages corrompus sont dirigés vers des files d'attente de lettres mortes pour inspection.

  5. Dégradation gracieuse
    Si le fournisseur d'e-mail est en panne, mettez en file d'attente l'e-mail et continuez les notifications dans l'application. Si les fournisseurs de push sont lents, livrez via websocket/dans l'application et retentez le push plus tard. Si le cache des préférences est indisponible, rabattez-vous sur la lecture des préférences durables ou sur des valeurs par défaut conservatrices. Si la présence est indisponible, sautez le websocket et fiez-vous au push/historique.

  6. Réplication et sauvegardes
    Utilisez des bases de données multi-AZ, des sauvegardes régulières, la récupération à un instant T pour les magasins relationnels et des procédures de restauration testées. Pour Cassandra/ScyllaDB, utilisez un facteur de réplication de 3 entre les AZ et des paramètres de quorum appropriés aux compromis latence/fiabilité.

  7. Surveillance et SLO
    Suivez la latence de bout en bout p50/p95/p99 depuis la création de l'événement source jusqu'à la réception par le client. Alertez sur le décalage de la file d'attente, les pics d'échec de livraison, la limitation du fournisseur, les partitions chaudes de la base de données, le taux de déconnexion websocket, la pression mémoire de Redis et les tempêtes de rééquilibrage des consommateurs.

API d'historique des notifications :

GET dernières notifications :
Le client demande les 100 dernières notifications. L'API Gateway authentifie l'utilisateur, l'API de notification interroge UserNotifications par user_id ordonné par created_at descendant, enrichit les champs d'affichage manquants depuis le cache et renvoie des enregistrements structurés.

Marquer la notification comme lue :
L'API met à jour read_at pour cette notification et ajuste le compte de non lus de manière idempotente.

Marquer tout comme lu :
Stocke last_read_timestamp pour l'utilisateur et met à jour de manière asynchrone les anciens enregistrements non lus. Cela évite les écritures synchrones volumineuses.

Goulots d'étranglement potentiels et compromis :

  1. Explosion de la diffusion (Fan-out explosion)
    Problème : Certains événements peuvent notifier de nombreux utilisateurs, submergeant les files d'attente et le stockage.
    Atténuation : Diffusion hybride, files d'attente prioritaires, agrégation, limites de débit, matérialisation paresseuse.
    Compromis : La diffusion à la lecture réduit la charge d'écriture mais complexifie les lectures et peut augmenter la latence de lecture.

  2. Utilisateurs et publications chauds
    Problème : Les célébrités ou les publications virales peuvent générer un volume de notifications énorme pour un destinataire ou un objet.
    Atténuation : Agréger les notifications par objet et par fenêtre de temps, partitionner les clés d'agrégation, supprimer les notifications répétées de faible valeur.
    Compromis : Les utilisateurs peuvent recevoir des notifications moins granulaires.

  3. Limites des fournisseurs de push
    Problème : APNs/FCM/fournisseurs d'e-mail peuvent limiter ou échouer.
    Atténuation : Agents de fournisseur dédiés, limitation de débit adaptative, nouvelles tentatives, nettoyage des jetons, backoff spécifique au fournisseur.
    Compromis : La livraison push externe ne peut pas toujours respecter la cible de moins de 2 secondes ; la livraison websocket pour les utilisateurs en ligne est le chemin fiable à faible latence.

  4. Volume d'écriture de la base de données
    Problème : Les écritures de notification par destinataire à grande échelle peuvent être coûteuses.
    Atténuation : Utiliser des magasins à colonnes larges optimisés pour l'écriture, regrouper les écritures, TTL, agrégation, diffusion hybride.
    Compromis : Les magasins à colonnes larges ont une flexibilité de requête limitée ; la conception doit correspondre aux modèles d'accès.

  5. Exactitude du compte de non lus
    Problème : Les compteurs mis en cache peuvent dériver sous les nouvelles tentatives et les mises à jour de lecture concurrentes.
    Atténuation : Mises à jour idempotentes, marqueurs de lecture, réconciliation périodique.
    Compromis : Le compte exact de non lus est plus coûteux qu'un compte de non lus éventuellement cohérent.

  6. Ordre
    Problème : Les notifications peuvent arriver dans le désordre en raison du traitement distribué.
    Atténuation : Utiliser des identifiants triables et ordonner par heure d'événement/identifiant de notification à la lecture. Partitionner par destinataire pour un ordre plus fort par utilisateur si nécessaire.
    Compromis : L'ordre global strict n'est ni pratique ni nécessaire.

  7. Latence versus durabilité
    Problème : Écrire l'historique avant la livraison améliore la fiabilité mais ajoute de la latence.
    Atténuation : Utiliser un stockage rapide en région locale et paralléliser le stockage/la livraison après l'attribution de l'idempotence.
    Compromis : La livraison parallèle peut brièvement afficher une notification push avant qu'elle n'apparaisse dans l'historique si le stockage est retardé.

  8. Complexité de l'évaluation des préférences
    Problème : Les vérifications de préférences par événement peuvent être coûteuses au pic.
    Atténuation : Mettre en cache les préférences, pré-calculer les paramètres effectifs et invalider le cache lors des changements de préférences.
    Compromis : Les changements de préférences peuvent prendre quelques secondes à se propager, sauf si une cohérence forte est requise.

Résumé de la conception finale recommandée :

Utiliser une architecture pilotée par les événements avec Kafka/Pulsar comme colonne vertébrale, des services d'ingestion/diffusion/routage sans état, Cassandra/ScyllaDB/DynamoDB pour l'historique des notifications par utilisateur, Redis pour la présence et les caches chauds, des passerelles websocket pour la livraison en ligne en moins de 2 secondes, APNs/FCM/Web Push pour les notifications de plateforme, et un pipeline de livraison d'e-mails séparé. Le système doit être scalable horizontalement, partitionné principalement par user_id, résilient grâce à des files d'attente durables et un traitement idempotent, et optimisé pour une faible latence grâce à la mise en cache, au routage basé sur la présence en ligne, aux files d'attente prioritaires et à l'agrégation. Cette conception répond aux exigences fonctionnelles, prend en charge les 1 milliard d'événements quotidiens attendus avec des pics de 5x, et offre une voie pratique vers une disponibilité de 99,9 % avec une dégradation gracieuse en cas de défaillances partielles.

Résultat

#2

Votes gagnants

0 / 3

Score moyen

86
Modèles évaluateurs OpenAI GPT-5.6

Score total

90

Commentaire global

La réponse B est également une conception excellente et complète. Elle fournit un pipeline cohérent piloté par les événements, introduit correctement le modèle outbox, modélise soigneusement les alternatives pour l'historique et l'état non lu, et traite de manière approfondie l'idempotence, la dégradation gracieuse, le fan-out hybride et les compromis latence-durabilité. Sa principale faiblesse relative est que plusieurs choix d'infrastructure et stratégies régionales restent des alternatives plutôt qu'un plan de déploiement concret unique. Elle fournit également moins de planification de capacité spécifique pour les connexions persistantes, les nombres de partitions, la marge et le comportement de basculement que la réponse A.

Afficher le détail de l’évaluation

Qualité de l’architecture

Poids 30%
89

L'architecture est cohérente et bien décomposée, et l'outbox transactionnel explicite ferme une fenêtre importante de perte d'événements. Le fan-out, l'évaluation des politiques, le stockage, la présence, les passerelles temps réel, les workers de canal, les modèles et la déduplication sont tous placés de manière appropriée. Elle perd une petite quantité de précision car l'ordre stockage-livraison et l'architecture régionale actif-actif par rapport à actif-passif sont présentés comme des options plutôt que des décisions de conception résolues.

Complétude

Poids 20%
91

Elle couvre la portée fonctionnelle et non fonctionnelle complète, y compris toutes les plateformes de livraison, les API d'historique, les modèles de données, la sémantique non lue, l'enregistrement des appareils, la présence, la localisation, la gestion des fournisseurs, la surveillance, les goulots d'étranglement et les compromis. Elle est légèrement moins complète opérationnellement car elle manque de dimensionnement détaillé des connexions persistantes, de provisionnement concret des partitions et d'une topologie multirégion entièrement sélectionnée.

Analyse des compromis

Poids 20%
92

Elle fournit une excellente analyse du fan-out à l'écriture par rapport au fan-out à la lecture, des limitations des requêtes à colonnes larges, de la précision du compteur non lu, de l'ordre, de la cohérence du cache de préférences, de la latence du fournisseur et de la latence durabilité-livraison. Le raisonnement est techniquement mature, bien que quelques alternatives restent ouvertes sans sélection finale ni seuil.

Scalabilité et fiabilité

Poids 20%
90

Elle calcule correctement les débits de base et de pointe et utilise le partitionnement, la mise à l'échelle horizontale, la contre-pression des files d'attente, l'isolation des priorités, le fan-out hybride, la réplication, l'idempotence, les nouvelles tentatives, les DLQ, les sauvegardes et la dégradation gracieuse. L'outbox est une force majeure en matière de fiabilité. Par rapport à A, elle est moins concrète sur la capacité de la flotte de connexions, la marge de mise à l'échelle à chaud, les nombres de partitions, les objectifs de récupération et la conception exacte du basculement régional.

Clarté

Poids 10%
86

La réponse est organisée logiquement et explique de manière cohérente chaque composant et décision. Elle est quelque peu répétitive et présente fréquemment des menus de technologies équivalentes ou d'approches de déploiement, ce qui affaiblit la décision et rend l'architecture finale légèrement plus difficile à extraire.

Modèles évaluateurs Google Gemini 2.5 Pro

Score total

87

Commentaire global

Une réponse très solide et techniquement aboutie qui présente une architecture correcte et robuste pour le système de notification. Elle couvre tous les aspects requis de la conception avec de bons détails, notamment dans la description des composants et la modélisation des données. Les solutions proposées sont conformes aux normes de l'industrie et bien justifiées. Sa principale faiblesse, par rapport à la Réponse A, est qu'elle est légèrement moins soignée dans sa structure, moins détaillée dans son analyse quantitative initiale et manque du contexte opérationnel supplémentaire fourni par un plan de déploiement.

Afficher le détail de l’évaluation

Qualité de l’architecture

Poids 30%
90

L'architecture proposée est également très solide et suit les meilleures pratiques pour un système piloté par les événements. Les composants sont logiques et leurs responsabilités sont bien définies. Elle identifie correctement le besoin d'un modèle de diffusion hybride (fan-out). La conception globale est robuste et bien adaptée à la tâche, bien que légèrement moins détaillée dans les interactions des composants que la Réponse A.

Complétude

Poids 20%
85

La réponse est très complète et aborde toutes les exigences fondamentales de la question, y compris l'architecture, les modèles de données et la scalabilité. Cependant, elle manque des sections supplémentaires, très pertinentes, sur un déploiement progressif et une liste structurée de métriques/SLO que fournit la Réponse A, ce qui la rend légèrement moins complète dans un sens pratique.

Analyse des compromis

Poids 20%
90

La réponse fournit une discussion solide sur les goulots d'étranglement potentiels et les compromis impliqués. Elle identifie correctement les problèmes tels que l'explosion de la diffusion (fan-out explosion) et le volume d'écriture de la base de données. Le raisonnement est solide et couvre les compromis clés, tels que la diffusion à la lecture (fan-out-on-read) par rapport à la diffusion à l'écriture (fan-out-on-write), ce qui en fait une section très solide.

Scalabilité et fiabilité

Poids 20%
85

La réponse présente un ensemble solide de stratégies pour la scalabilité et la fiabilité, y compris le partitionnement, la mise à l'échelle horizontale, la contre-pression (backpressure) et le modèle outbox. Tout cela est correct et approprié pour le système. Cependant, les stratégies sont décrites en termes légèrement plus généraux par rapport aux plans spécifiques et actionnables de la Réponse A.

Clarté

Poids 10%
80

La réponse est bien écrite et structurée, ce qui la rend généralement facile à comprendre. Cependant, la prose est plus dense que dans la Réponse A, et certains concepts sont répétés dans différentes sections. La mise en forme supérieure et la concision de la Réponse A lui donnent un net avantage en termes de lisibilité.

Modèles évaluateurs Anthropic Claude Fable 5

Score total

80

Commentaire global

La réponse B est une conception approfondie et techniquement solide couvrant toutes les exigences de l'invite : pipeline piloté par les événements, fan-out-on-write vs fan-out hybride, magasin d'historique à colonnes larges avec une justification claire, présence de Redis, travailleurs de canal et une liste de huit goulots d'étranglement/compromis bien organisée. Elle inclut de manière unique le modèle de boîte d'envoi transactionnelle pour la fiabilité du producteur et une alternative réfléchie de marqueur de lecture pour les messages non lus. Cependant, elle est moins étayée quantitativement que A (pas de dimensionnement du stockage, pas de dimensionnement de la couche de connexion, pas de conseils sur l'approvisionnement des partitions), hésite fréquemment entre les options (Kafka ou Pulsar, Postgres ou MySQL ou DynamoDB) plutôt que de s'engager avec des justifications, et manque de profondeur opérationnelle comme les signaux de mise à l'échelle automatique, la stratégie de déploiement/déploiement et les paramètres de cohérence concrets liés à la cible de disponibilité.

Afficher le détail de l’évaluation

Qualité de l’architecture

Poids 30%
80

Architecture événementielle très solide avec des composants bien définis, un fan-out hybride, un routage basé sur la présence et le modèle de boîte d'envoi transactionnelle, qui manque à A. Cependant, elle s'engage moins fermement (Kafka ou Pulsar, plusieurs options de base de données proposées sans choix final), ne fournit aucun dimensionnement de la couche de connexion ou de l'approvisionnement des partitions, et le chemin critique de livraison est moins explicitement conçu pour le budget de latence.

Complétude

Poids 20%
85

Aborde tous les aspects requis : architecture, composants, modèle de données avec exemples de schémas de table, pile technologique, scalabilité, fiabilité, flux d'API d'historique et huit éléments de goulot d'étranglement/compromis. Inclut le modèle de boîte d'envoi et le modèle de marqueur de lecture en extras. Légèrement moins complète que A en matière de dimensionnement de capacité/stockage, de considérations de conformité, de stratégie de déploiement et de cibles SLO définies.

Analyse des compromis

Poids 20%
78

Un format problème/atténuation/compromis bien structuré sur huit goulots d'étranglement, couvrant le fan-out, l'ordre, la latence par rapport à la durabilité, et la dérive des compteurs. Bonne étendue, mais les compromis sont plus courts et plus descriptifs ; il pèse rarement explicitement les alternatives (par exemple, pourquoi Kafka plutôt qu'une file d'attente plus simple, ou pourquoi une persistance polyglotte) avec la profondeur dont A fait preuve.

Scalabilité et fiabilité

Poids 20%
78

Couvre les bons mécanismes : partitionnement par destinataire, mise à l'échelle horizontale, backpressure via les files d'attente, isolation des priorités par canal, options multi-régions, nouvelles tentatives avec backoff, DLQ et dégradation gracieuse. Cependant, les conseils sont plus génériques ; il n'y a pas de signaux de mise à l'échelle automatique, de chiffres de marge, de spécificités de partitions chaudes, ou de pratiques de validation (tests de charge, chaos) reliant la conception aux cibles énoncées.

Clarté

Poids 10%
80

Sectionnement clair avec des composants numérotés et un résumé final utile. Lisible dans l'ensemble, bien que les hésitations fréquentes entre les options technologiques et certaines répétitions entre les sections de scalabilité, de latence et de fiabilité diluent légèrement le message.

Résumé comparatif

Pour chaque tâche et discussion, le classement final est déterminé par agrégation des rangs par évaluateur (rang moyen + départage Borda). Le score moyen est affiché à titre indicatif.

Évaluateurs: 3

Votes gagnants

3 / 3

Score moyen

92
Voir cette réponse

Votes gagnants

0 / 3

Score moyen

86
Voir cette réponse

Résultats de l’évaluation

Modèles évaluateurs Anthropic Claude Fable 5

Raison du gagnant

La réponse A l'emporte sur les critères pondérés. Sur le critère le plus important, la Qualité de l'Architecture (30%), A fournit des décisions de conception plus concrètes et engagées : niveau WebSocket dimensionné, partitions Kafka provisionnées avec paramètres de réplication, voies prioritaires pour l'indicateur de niveau de service de latence, et un chemin de livraison entièrement spécifié avec échelle de repli. Sur le Raisonnement des Compromis (20%), les alternatives et rejets explicites de A (Kafka vs SQS, exactement une fois vs au moins une fois, polyglotte vs magasin unique, région unique vs actif-actif) dépassent la liste d'atténuation de B. Sur la Scalabilité/Fiabilité (20%), A donne des mécanismes exploitables (mise à l'échelle KEDA sur le décalage du consommateur, compaction TWCS, clés chaudes salées, tests de chaos, validation de charge 5x) où B reste plus générique. Le modèle outbox et le modèle de marqueur de lecture de B sont des avantages réels, mais ils ne compensent pas la supériorité de A sur les critères les plus pondérés. Le résultat pondéré favorise clairement A.

Modèles évaluateurs Google Gemini 2.5 Pro

Raison du gagnant

La réponse A l'emporte car elle propose une conception plus détaillée, soignée et complète. Bien que les deux réponses proposent une architecture similaire et solide, la réponse A excelle dans plusieurs domaines clés. Elle commence par une estimation approfondie de la capacité quantitative, ce qui ancre l'ensemble de la conception dans des chiffres concrets. Sa discussion sur la scalabilité et la fiabilité est plus spécifique, mentionnant des outils et des pratiques avancés comme KEDA pour l'autoscaling et le chaos engineering. Elle est exceptionnellement claire et bien structurée, rendant les informations complexes faciles à assimiler. Enfin, elle va au-delà des exigences de la consigne en incluant un plan de déploiement progressif et une section dédiée aux métriques/SLO, démontrant une approche plus complète et prête pour la production.

Modèles évaluateurs OpenAI GPT-5.6

Raison du gagnant

La réponse A l'emporte avec une légère marge pondérée. Bien que la réponse B ait un argument plus solide en matière de fiabilité des événements sources grâce à son modèle de boîte d'envoi explicite, la réponse A est plus complète et concrète sur le plan opérationnel en ce qui concerne la planification de la capacité, l'isolement des priorités, la mise à l'échelle des WebSockets, la configuration du datastore, les contrôles de latence, les mécanismes de haute disponibilité, l'atténuation des goulots d'étranglement, le déploiement et les SLO. Ces avantages sont particulièrement significatifs en termes de complétude et de scalabilité/fiabilité, tandis que son architecture reste très solide malgré le manque de détails sur la publication transactionnelle.

X f L