Aller au contenu
Antoine Le LouëtDéveloppeur senior indépendant (Accueil)
← Revenir au Journal

Conseils9 min de lecture

11 patterns de System Design à connaître en 2026

Load balancer, CDN, files de messages, CQRS : quand utiliser ces onze patterns d’architecture, et quelles contraintes prévoir avant de les ajouter ?

Onze patterns répartis par usage : trafic, protection, données et traitements asynchrones.
Un même composant peut remplir plusieurs rôles. Ces familles aident à repérer le problème à résoudre, sans imposer une architecture.

Partir du symptôme de l’application

Une API ralentit pendant une campagne commerciale. Faut-il ajouter un serveur, mettre les réponses en cache ou déplacer un traitement dans une file ? Ces choix répondent à des contraintes différentes. Je propose de parcourir onze patterns de System Design en reliant chacun à une situation concrète et au coût qu’il ajoute.

La sélection couvre le réseau, mais aussi le stockage et l’organisation des traitements. CQRS et le sharding dépassent ainsi le seul routage des requêtes. Les exemples décrivent des situations fictives.

1. Load balancer : répartir les requêtes

Un répartiteur de charge distribue le trafic entre plusieurs instances d’un service. Il peut alterner les requêtes entre les serveurs, ou tenir compte des connexions en cours. Pour une application web dont le serveur sature, il permet d’utiliser plusieurs instances, à condition que celles-ci puissent traiter les mêmes demandes.

La haute disponibilité demande aussi de détecter les instances défaillantes et de prévoir la panne du répartiteur lui-même. Les sessions stockées uniquement dans la mémoire d’une instance compliquent le basculement. Ajouter des serveurs applicatifs ne corrige pas non plus une base déjà saturée. Je commencerais donc par identifier où les requêtes attendent réellement.

2. CDN : rapprocher le contenu des utilisateurs

Un Content Delivery Network sert des copies de contenu depuis des points de présence répartis géographiquement. Une image ou un fichier JavaScript déjà en cache peut être livré sans solliciter le serveur d’origine. C’est utile pour un catalogue consulté depuis plusieurs régions ou pour distribuer des médias. Le point choisi dépend du routage et de la disponibilité, pas seulement de la distance sur une carte.

Il faut définir ce qui peut être partagé et pendant combien de temps. Des fichiers statiques dont le nom contient une empreinte peuvent rester longtemps en cache. Une réponse personnalisée exige une politique adaptée : no-store interdit son stockage, tandis que private interdit son stockage dans un cache partagé. Une réponse absente du cache doit encore être récupérée à l’origine.

3. Message queue : différer le travail

Une file de messages sépare la réception d’une demande de son traitement. Dans un export volumineux, l’API peut enregistrer un travail à effectuer, puis un consommateur produit le fichier. La file absorbe un pic temporaire si les consommateurs peuvent ensuite rattraper le retard. Une arrivée durablement supérieure à leur capacité fait croître l’attente.

La fiabilité dépend de la configuration de durabilité et des confirmations de publication, puis des acquittements après traitement. Une livraison peut se répéter : le consommateur doit éviter de créer deux fois la même facture, par exemple en reconnaissant un identifiant déjà traité. Il faut aussi prévoir le sort des messages qui échouent durablement.

RabbitMQ propose notamment des files de travail. Kafka repose sur des journaux d’événements partitionnés et conservés selon une politique de rétention ; les consommateurs suivent leur position de lecture. Il peut répartir du travail au sein d’un groupe, mais sa relecture et ses groupes indépendants lui donnent des usages différents d’une file consommée puis acquittée.

4. Rate limiter : fixer un budget de requêtes

Un limiteur contrôle le débit autorisé pour un utilisateur, une clé d’API ou une autre identité. On peut, par exemple, fixer un quota par minute sur une route coûteuse, avec une tolérance pour les petites rafales. Cette règle aide à partager les ressources et à contenir certains abus. Une réponse HTTP 429 indique un dépassement ; Retry-After peut préciser le délai avant une nouvelle tentative.

Le choix de la clé compte : une limite par adresse IP peut pénaliser plusieurs personnes derrière le même accès réseau. Avec plusieurs instances, un compteur local par serveur multiplie potentiellement le quota effectif. Il faut décider comment coordonner les limites et quel comportement adopter si le stockage des compteurs devient indisponible.

5. Circuit breaker : interrompre les appels voués à échouer

Lorsqu’une dépendance accumule les erreurs, un disjoncteur ouvre le circuit : les appels suivants échouent rapidement pendant un délai défini. Une phase de test laisse ensuite passer quelques requêtes pour évaluer la reprise. Ce mécanisme limite les ressources immobilisées par une API externe défaillante et aide à contenir les pannes en cascade.

Le service appelant doit prévoir ce que voit l’utilisateur pendant l’interruption. Une estimation de livraison peut devenir temporairement indisponible ; un paiement incertain doit conserver un état explicite. Les délais d’attente restent nécessaires. Les nouvelles tentatives doivent être bornées et tenir compte du circuit ouvert pour éviter d’aggraver la panne.

6. Database sharding : répartir les données

Le sharding distribue des sous-ensembles de données entre plusieurs partitions hébergées sur des serveurs distincts. Une application multiclient pourrait utiliser l’identifiant d’organisation pour choisir le fragment. Cette approche devient pertinente lorsque les volumes ou le débit dépassent les capacités raisonnables d’un seul serveur, après examen des requêtes et des index.

La clé choisie détermine la répartition de la charge. Si une organisation concentre l’activité, son fragment peut saturer pendant que les autres restent peu utilisés. Les jointures et transactions entre fragments se compliquent, tout comme le rééquilibrage. La réplication conserve des copies ; le sharding répartit les données. On peut combiner les deux pour des objectifs différents.

Le sharding place A dans un fragment et B dans un autre. La réplication conserve A et B dans chaque copie. Le dessin distingue la répartition de données et la copie d’un même ensemble.
Répartition logique simplifiée. Les mécanismes de synchronisation, les retards éventuels des copies et la haute disponibilité ne sont pas représentés ; les deux approches peuvent être combinées.

7. Caching layer : éviter les lectures répétées

Un cache applicatif, par exemple avec Redis ou Memcached, conserve des données fréquemment consultées. Avec le modèle cache-aside, l’application cherche d’abord dans le cache. En cas d’absence, elle lit la base, remplit le cache et renvoie la réponse. Un catalogue souvent lu et peu modifié peut bénéficier de cette approche.

Le point délicat est la fraîcheur. Une durée de vie limite l’ancienneté d’une entrée, sans garantir une lecture à jour après chaque écriture. Il faut organiser l’invalidation et prévoir la panne du cache : un retour massif vers la base peut la saturer. Je définirais d’abord le retard acceptable pour la donnée concernée, puis la stratégie d’expiration.

8. API gateway : organiser l’entrée des API

Une passerelle expose une adresse d’entrée stable devant plusieurs services. Elle route, par exemple, /catalogue vers un service et /commandes vers un autre. Selon sa configuration, elle peut vérifier des jetons d’authentification ou appliquer des quotas. Elle devient utile quand plusieurs API doivent présenter des règles communes aux clients.

Les services doivent conserver les autorisations métier liées aux données : pouvoir appeler une route ne suffit pas à autoriser la lecture de n’importe quelle commande. La passerelle ajoute aussi un élément critique sur le trajet. Sa disponibilité et ses changements de configuration demandent autant d’attention que ceux des services qu’elle expose.

9. Reverse proxy : relayer les appels côté serveur

Un reverse proxy reçoit les requêtes pour le compte des serveurs d’origine. Il peut terminer la connexion TLS, relayer les appels vers l’application et, selon sa configuration, compresser ou mettre en cache les réponses. C’est utile pour exposer une application tout en centralisant certains réglages HTTP.

Les rôles se recouvrent : un même NGINX peut agir comme reverse proxy et répartiteur de charge. Une API gateway peut s’appuyer sur ce fonctionnement en y ajoutant des politiques propres aux API. Ces termes ne désignent donc pas nécessairement des machines séparées. Il faut aussi contrôler les en-têtes transmis et empêcher l’accès direct à l’origine si la protection repose sur le proxy.

10. Event-driven architecture : réagir à un fait métier

Dans une architecture événementielle, un service publie un fait, tel que CommandeConfirmée. D’autres traitements peuvent y réagir pour envoyer une notification ou alimenter des statistiques. Le producteur n’a pas besoin de connaître tous les consommateurs. Une commande demande une action ; un événement décrit quelque chose qui s’est produit.

Une file est un mécanisme de transport possible ; l’architecture événementielle organise les interactions autour de ces faits. Il faut décider comment faire évoluer leur format, gérer les doublons et interpréter leur ordre. Le temps réel dépend du délai de livraison et de traitement. Un consommateur en retard ne donne pas une vue immédiatement à jour.

Une autre difficulté apparaît entre l’écriture en base et la publication : un incident peut laisser une commande enregistrée sans événement. Le pattern transactional outbox consiste à enregistrer le fait à publier dans la même transaction que la modification métier, puis à le transmettre séparément. Les consommateurs doivent toujours supporter les livraisons répétées.

La commande et le fait à publier sont enregistrés dans une même transaction de base. Un relais publie ensuite hors de cette transaction. L’envoi pouvant se répéter, le consommateur doit reconnaître les événements déjà traités.
La frontière dessinée concerne la transaction locale, pas un engagement atomique avec tous les services. Le schéma explique pourquoi les consommateurs doivent encore supporter les doublons.

11. CQRS : distinguer les modèles de lecture et d’écriture

CQRS (Command Query Responsibility Segregation) sépare les commandes qui modifient l’état des requêtes qui le consultent, avec des modèles adaptés à chaque usage. Dans une réservation, l’écriture vérifie les règles de disponibilité. La lecture peut proposer une représentation préparée pour afficher le planning. Cette séparation devient intéressante lorsque les règles métier et les besoins de consultation divergent fortement.

Les deux modèles peuvent partager la même base. CQRS n’impose ni Kafka, ni l’event sourcing, qui conserve les changements d’état sous forme d’événements. Si une projection de lecture est alimentée de façon asynchrone, elle peut afficher un état en retard. Il faut accepter ce délai et gérer la reconstruction des projections. Pour un CRUD simple, le coût de cette séparation peut dépasser son intérêt.

Ce que j’ajouterais à la liste

Pour concevoir une application, je compléterais cette sélection par les délais d’attente et les nouvelles tentatives avec attente progressive et part aléatoire, afin d’éviter que tous les clients recommencent ensemble. L’idempotence, qui permet de répéter une opération sans répéter son effet métier, devient essentielle dès qu’un résultat réseau reste incertain.

Avant d’ajouter un composant, prenez un parcours précis, par exemple la validation d’une commande. Notez chaque appel distant, le délai maximal acceptable et ce que l’utilisateur verra en cas d’échec. Mesurez ensuite les attentes et les erreurs sur ce parcours. Cela permet de choisir un premier pattern sur un problème observable, puis de vérifier son effet.

Sources et liens utiles

Partager sur LinkedIn (nouvel onglet)

Poursuivre la lecture
Tous les articles de la catégorie →