Une collection éditoriale pour développeurs qui veulent comprendre l'architecture sans collectionner les patterns. Un numéro = un choix de conception, disséqué jusqu'à ce que le tradeoff soit nu. Le problème d'abord, le prix ensuite, et quand ne pas l'employer — pas un pattern de plus à mémoriser.An editorial collection for developers who want to understand architecture without hoarding patterns. One issue = one design choice, dissected until the tradeoff is bare. The problem first, the cost next, and when not to use it — not one more pattern to memorize.
Avant de choisir un pattern, on identifie l'axe qui va bouger — la donnée, le protocole, la règle — et on place la couture exactement là. Pas à l'endroit le plus complexe : à l'endroit le plus volatil.Before choosing a pattern, identify the axis that will move — the data, the protocol, the rule — and put the seam exactly there. Not at the most complex spot: at the most volatile one.
Le contre-poids du numéro précédent. Chaque couche ajoute un saut : un nom à apprendre, un fichier à ouvrir, un cadre de pile à traverser. Elle ne vaut son loyer que si elle décide quelque chose.The counterweight to the previous issue. Every layer adds a hop: a name to learn, a file to open, a stack frame to step through. It only earns its rent if it decides something.
Le numéro qui referme le triptyque fondateur. Toutes les décisions ne se valent pas : certaines portes se rouvrent, d'autres se murent à mesure que le monde s'appuie dessus. Savoir laquelle on franchit décide combien de réflexion elle mérite.The issue that closes the founding triptych. Not all decisions are equal: some doors reopen, others wall shut as the world leans on them. Knowing which one you're walking through decides how much thought it deserves.
La règle « sois paresseux » coupe la plomberie — et l'organe avec, parce qu'elle ne compte que les lignes. Avant de tailler, classe : accessoire ou essentiel ? L'un se taille au bistouri, l'autre se juge par ce qu'il protège.The 'be lazy' rule cuts the plumbing — and the organ with it, because it only counts lines. Before cutting, classify: incidental or essential? One is trimmed with the scalpel, the other is judged by what it protects.
Un mauvais architecte veut être le plus intelligent de la pièce ; un bon rend les autres plus intelligents, et son travail réussi est presque invisible. Ce numéro tire le fil : la valeur d'un architecte, c'est de réduire le risque dominant — pas de suivre le plan ; de dessiner la carte avant de trancher le débat ; d'éclairer la décision suivante plutôt que de figer un grand plan ; et de gagner sa crédibilité avant de la dépenser.A bad architect wants to be the smartest person in the room; a good one makes everyone else smarter, and his successful work is nearly invisible. This issue pulls the thread: an architect's value is to reduce the dominant risk — not to follow the plan; to draw the map before settling the debate; to light the next decision rather than freeze a grand plan; and to earn his credibility before spending it.
Le code naïf fait pointer la règle métier vers le détail technique : la commande « connaît » SMTP. Le jour où SMTP change, le métier change. L'inversion ne déplace pas le code, elle retourne une flèche — et soudain le métier ne connaît plus personne.Naive code makes the business rule point to the technical detail: the order 'knows' SMTP. The day SMTP changes, the business changes. The inversion doesn't move code, it flips an arrow — and suddenly the business knows no one.
Le #1 retournait une flèche. Une vraie application en a des dizaines — base, paiement, mail, file. On cesse d'empiler des couches et on dessine une frontière fermée autour du métier : ce qui la traverse passe par un port qu'il possède, l'infra n'est plus qu'un adapter aux bords. Hexagonal, Onion, Clean : trois noms, une idée.Issue #1 flipped one arrow. A real application has dozens — database, payment, mail, queue. Stop stacking layers and draw a closed boundary around the business: what crosses it goes through a port it owns, infra becomes mere adapters at the edges. Hexagonal, Onion, Clean: three names, one idea.
Présentation, Métier, Données : le découpage le plus ancien et le plus utile. Sa valeur n'est pas dans les trois boîtes, mais dans une règle de savoir — chaque couche ne connaît que sa préoccupation. Quand une remise se calcule dans la vue et se recalcule dans le SQL, le découpage ne sépare plus rien : c'est la couche anémique, l'anti-pattern qui ressemble à du code propre.Presentation, Business, Data: the oldest and most useful split. Its value isn't in the three boxes but in a rule of knowledge — each layer knows only its concern. When a discount is computed in the view and recomputed in the SQL, the split separates nothing: that's the anemic layer, the anti-pattern that looks like clean code.
Clean dit dans quel sens pointent les dépendances ; Vertical Slices dit le long de quelle ligne on plie l'arborescence. Ranger par couche rend le changement de techno local et la feature dispersée ; ranger par feature fait l'inverse. Aucune découpe n'est gratuite — et la réponse mûre n'est pas un camp, c'est : slices au sommet, règle de dépendance à l'intérieur de celles qui portent un vrai métier.Clean says which way dependencies point; Vertical Slices says along which line you fold the file tree. Cutting by layer makes a tech swap local and a feature scattered; cutting by feature does the reverse. No cut is free — and the mature answer isn't a camp, it's: slices on top, the dependency rule inside those that carry real business.
Un handler qui n'existe que pour débiter une carte finit par authentifier, limiter, journaliser, valider, tracer — six préoccupations transverses entrelacées avec sa seule ligne de métier, recopiées dans chaque endpoint. La Chain of Responsibility — dont le middleware HTTP est une incarnation — sort chaque transverse dans un maillon autonome : la requête traverse une chaîne explicite, et le cœur n'est que le dernier maillon. On gagne réutilisation, test du cœur seul, ordre lisible. Le prix subtil : la chaîne devient si pratique qu'on y cache le flux de contrôle. Le middleware enveloppe la requête, il ne décide pas du métier.A handler that exists only to charge a card ends up authenticating, throttling, logging, validating, tracing — six cross-cutting concerns interwoven with its single business line, recopied in every endpoint. The Chain of Responsibility — of which HTTP middleware is one incarnation — lifts each concern into a standalone link: the request crosses an explicit chain, and the core is just the last link. You gain reuse, testing the core alone, a readable order. The subtle price: the chain becomes so convenient you hide control flow in it. Middleware wraps the request, it doesn't decide the domain.
Un if/else qui grandit d'une branche à chaque nouveau cas transforme une fonction stable en liste ouverte qu'on rouvre à l'infini. Strategy nomme ce qui varie — la formule — l'extrait derrière un contrat et la passe au code qui s'en sert. Puis le fil rouge du volume : quand le contrat n'a qu'une méthode, l'objet n'était qu'une fonction déguisée.An if/else that grows a branch per case turns a stable function into an open list you reopen forever. Strategy names what varies — the formula — extracts it behind a contract and passes it to the code that uses it. Then the volume's red thread: when the contract has a single method, the object was just a function in disguise.
Une fonction métier qui énumère ses réactions — notifier, tracer, réserver — soude le fait stable à la liste volatile de ceux qui y réagissent. Observer inverse l'abonnement : les intéressés s'inscrivent, l'émetteur publie un fait sans savoir qui l'écoute. Puis le fil rouge du volume — l'observateur à une méthode n'était qu'un callback — et l'avertissement : inverser la dépendance ne suffit pas si la chaîne reste synchrone.A business function that lists its reactions — notify, track, reserve — welds the stable fact to the volatile list of those who react to it. Observer inverts the subscription: the interested parties register, the emitter publishes a fact without knowing who listens. Then the volume's red thread — the one-method observer was just a callback — and the warning: inverting the dependency isn't enough if the chain stays synchronous.
Un code qui écrit new sur des classes concrètes et assemble ses objets à la main mêle deux savoirs étrangers à sa mission : quel type créer, et comment le monter. Factory isole le choix du type, Builder isole l'assemblage — et ne livre que des objets valides. Puis le fil rouge du volume, poussé à son terme : une Factory n'est qu'une fonction, un Builder des arguments nommés, et le Singleton, l'écarté qui déguise un état global en pattern.Code that writes new on concrete classes and assembles its objects by hand mixes two concerns foreign to its mission: which type to create, and how to assemble it. Factory isolates the type choice, Builder isolates the assembly — and delivers only valid objects. Then the volume's red thread, pushed to its end: a Factory is just a function, a Builder named arguments, and the Singleton, the outcast that dresses up global state as a pattern.
status est une chaîne, et n'importe quelle méthode lui assigne n'importe quelle valeur : on annule une commande expédiée, on rembourse ce qui n'a jamais été payé. La règle « une commande expédiée ne s'annule pas » vit en gardes éparses, supposée plus que vérifiée — jusqu'au jour où l'illégal arrive en prod. La machine à états ferme l'ensemble en états finis, déclare en un seul endroit les transitions permises, et en fait la seule porte : une transition absente n'est pas refusée, elle est irreprésentable. Le piège des deux côtés : une machine décorative qu'un setter public contourne, ou un statechart à douze états pour un booléen. La machine ne vaut que si elle est la seule porte.status is a string, and any method assigns it any value: you cancel a shipped order, you refund what was never paid. The rule 'a shipped order isn't cancellable' lives in scattered guards, assumed more than enforced — until the day the illegal reaches prod. The state machine closes the set into finite states, declares the permitted transitions in one place, and makes that the only door: an absent transition isn't refused, it's unrepresentable. The trap on both sides: a decorative machine a public setter bypasses, or a twelve-state statechart for a boolean. The machine is only worth it if it's the only door.
Enchaîner quatre appels en synchrone les traite comme s'ils comptaient pareil : leurs latences s'additionnent, leurs disponibilités se multiplient, et un service périphérique en panne fait échouer un paiement réussi. Or un seul appel gate la réponse ; le reste doit seulement être fait. Couper le fil après le gate découple dans le temps — mais l'async n'est pas « mieux » : c'est un échange, et on doit reconstruire (file durable, retry, idempotence) la garantie qu'offrait le synchrone.Chaining four calls synchronously treats them as if they counted the same: their latencies add up, their availabilities multiply, and one peripheral outage fails a successful payment. Yet only one call gates the response; the rest merely have to happen. Cutting the thread after the gate decouples in time — but async isn't 'better': it's a trade, and you must rebuild (durable queue, retry, idempotence) the guarantee synchronous gave for free.
Livrer un fait à chaque consommateur en direct transmet le pic tel quel et perd l'événement quand un service est absent. Intercaler une file de messages pose un tampon durable : elle amortit les pics, survit aux pannes, diffuse à N consommateurs — Observer (Vol 3) durci et réseauté. Mais la file ne promet qu'« au moins une fois, sans ordre » : tout consommateur doit être idempotent et tolérant au désordre, sous peine de bâtir une machine à doublons.Delivering a fact to each consumer directly passes the spike through as-is and loses the event when a service is absent. Inserting a message queue lays a durable buffer: it absorbs spikes, survives outages, broadcasts to N consumers — Observer (Vol 3) hardened and networked. But the queue promises only 'at-least-once, no order': every consumer must be idempotent and reorder-tolerant, or you build a duplicate machine.
Une transaction en plusieurs étapes — payer, réserver, expédier, notifier — peut s'enchaîner par événements (chorégraphie) ou être tenue par un coordinateur (orchestration). La chorégraphie séduit par son découplage, mais dès que l'ordre est imposé et qu'il faut se défaire à l'échec, elle disperse une transaction sans propriétaire : invisible, figée, irréversible. Le vrai critère n'est pas « découplé vs centralisé » mais « suite de réactions indépendantes, ou transaction qui mérite un propriétaire ? ».A multi-step transaction — pay, reserve, ship, notify — can chain by events (choreography) or be held by a coordinator (orchestration). Choreography seduces with its decoupling, but the moment order is required and it must undo on failure, it scatters an ownerless transaction: invisible, frozen, irreversible. The real criterion isn't 'decoupled vs centralized' but 'a chain of independent reactions, or a transaction that deserves an owner?'.
Une règle métier — « on n'expédie qu'une commande payée » — soudée à un SELECT, une colonne, un driver : le stockage a fui dans le raisonnement. Le Repository glisse une frontière : le métier parle à une collection feinte (find, add, save) qu'il possède, et une seule classe derrière sait que c'est une base. C'est l'inversion de dépendance du Vol 1 pointée sur la persistance. Mais un objet nommé « Repository » qui laisse fuir l'ORM ne fait que déguiser le couplage : la frontière n'existe que si rien du stockage ne la traverse.A business rule — 'you only ship a paid order' — welded to a SELECT, a column, a driver: storage has leaked into the reasoning. The Repository slides in a boundary: the domain talks to a feigned collection (find, add, save) it owns, and one class behind it knows that's a database. It's Vol 1's dependency inversion aimed at persistence. But an object named 'Repository' that leaks the ORM merely disguises the coupling: the boundary only exists if nothing from storage crosses it.
Le Repository (#1) rangeait lecture et écriture derrière une frontière, en supposant un seul modèle. CQRS questionne ce postulat : écrire exige un agrégat riche et cohérent, lire un tableau de bord exige des lignes plates et rapides — un seul objet ne peut être bon aux deux. La réponse est un spectre : sa forme la moins chère est deux modèles dans la même base, presque gratuite ; sa forme radicale — deux stockages, projection asynchrone — n'achète l'échelle qu'en signant la cohérence à terme. On monte d'un cran sous une mesure, jamais sous une mode.The Repository (#1) tucked reads and writes behind a boundary, assuming one model. CQRS questions that: writing demands a rich, consistent aggregate, reading a dashboard demands flat, fast rows — one object can't be good at both. The answer is a spectrum: its cheapest form is two models in one database, nearly free; its radical form — two stores, async projection — buys scale only by signing for eventual consistency. You climb one notch under a measurement, never a fashion.
On a toujours stocké l'état présent — status = 'shipped' — et chaque UPDATE écrase le passé. Event Sourcing inverse l'idée : on stocke les faits en append seul (payée, expédiée, remboursée), et l'état devient le repli de cette suite. La force est réelle — audit gratuit, histoire rejouable. Mais c'est le pattern le plus sur-vendu de la série : le prix est permanent (versionner les événements, recalculer l'état, gérer snapshots et migrations), et l'asymétrie doit faire hésiter — on l'ajoute quand on veut, on ne le retire presque jamais. On ne source que l'agrégat où l'histoire est le produit.We've always stored present state — status = 'shipped' — and each UPDATE overwrites the past. Event Sourcing inverts it: you store facts append-only (paid, shipped, refunded), and state becomes the fold of that sequence. The strength is real — free audit, replayable history. But it's the series' most over-sold pattern: the price is permanent (versioning events, recomputing state, managing snapshots and migrations), and the asymmetry should make you hesitate — you add it whenever you like, you almost never remove it. You source only the aggregate where history is the product.
L'application tient en un seul processus — c'est sain. Mais à l'intérieur, « commandes », « facturation » et « stock » ne sont que des dossiers qui lisent et écrivent les tables des autres : pas des modules, une boule de boue où renommer une colonne casse trois domaines. Le monolithe modulaire trace la couture là où passe le vrai métier : chaque module possède ses données et expose une API, un seul déployable mais des frontières réelles. Le piège des deux côtés : croire que séparer les processus crée les frontières (le monolithe distribué), ou sur-modulariser une appli qui tient dans une tête. La frontière logique d'abord ; le réseau, seulement si la douleur l'exige.The app fits in one process — that's healthy. But inside, 'orders', 'billing' and 'inventory' are just folders reading and writing each other's tables: not modules, a ball of mud where renaming a column breaks three domains. The modular monolith draws the seam where the real business runs: each module owns its data and exposes an API, one deployable but real boundaries. The trap on both sides: believing that separating processes creates the boundaries (the distributed monolith), or over-modularizing an app that fits in one head. The logical boundary first; the network, only if the pain demands it.
Le #1 a tracé la frontière en logique ; ce numéro la transforme en frontière réseau — l'appel qui était en mémoire devient un appel HTTP. Sauf que le réseau n'est pas une fonction : il peut être lent, échouer à moitié, ou répondre deux fois. Un microservice s'achète : ce qu'on obtient — le déploiement indépendant — répond à un problème d'organisation, pas à une vertu technique ; ce qu'on paie est réel (le réseau devient le bug, l'ACID se mue en cohérence éventuelle). Le piège des deux côtés : la granularité absurde et le démarrage prématuré. On n'extrait un service que quand la douleur le justifie — pas avant.Issue #1 drew the boundary in logic; this one turns it into a network boundary — the in-memory call becomes an HTTP call. Except the network isn't a function: it can be slow, fail halfway, or answer twice. A microservice is bought: what you get — independent deployment — answers an organizational problem, not a technical virtue; what you pay is real (the network becomes the bug, ACID turns into eventual consistency). The trap on both sides: absurd granularity and premature start. You extract a service only when the pain justifies it — not before.
Le Vol 6 a tracé la frontière réseau ; ce numéro affronte ce qu'elle rend inévitable. Un appel entre services peut être lent, expirer, ou échouer alors que le service va très bien — le renversement qui fonde le volume : l'échec transitoire n'est pas une exception rare, c'est un état normal du fil. Trois outils le domptent : un timeout qui borne l'attente, un retry qui absorbe le transitoire, un backoff avec jitter qui espace les tentatives. Mais retenter un appel déjà abouti double l'effet — d'où l'idempotence. La règle : ne retente que ce qui est transitoire et idempotent, borné, à un seul étage — sinon échoue vite.Vol 6 drew the network boundary; this issue faces what it makes inevitable. A call between services can be slow, time out, or fail while the service is perfectly fine — the reversal that founds the volume: transient failure isn't a rare exception, it's a normal state of the wire. Three tools tame it: a timeout that bounds the wait, a retry that absorbs the transient, a backoff with jitter that spaces attempts. But retrying an already-landed call doubles the effect — hence idempotency. The rule: retry only what is transient and idempotent, bounded, at a single tier — otherwise fail fast.
Le #1 a appris à retenter le hoquet ; ce numéro affronte son angle mort — la panne franche et durable. Quand le service d'en face est vraiment mort plusieurs minutes, le retry seul devient nuisible : chaque requête paie le plein timeout, les pools saturent, et en martelant le service à terre on l'empêche de redémarrer. Le réflexe : un disjoncteur, machine à trois états — fermé, ouvert (on coupe le temps d'un cooldown), demi-ouvert (un appel test sonde le retour). Retry et disjoncteur ne se remplacent pas : ils répondent au transitoire et au soutenu, on les empile. La règle : un disjoncteur n'existe que s'il sait se refermer.Issue #1 taught retrying the hiccup; this one faces its blind spot — the hard, lasting outage. When the service across is truly dead for minutes, the retry alone turns harmful: every request pays the full timeout, pools saturate, and by hammering the downed service you keep it from restarting. The reflex: a circuit breaker, a three-state machine — closed, open (it cuts for a cooldown), half-open (a test call probes the return). Retry and breaker don't replace each other: they answer the transient and the sustained, you stack them. The rule: a breaker only exists if it can close again.
Les #1 et #2 ont laissé une dette : « et si l'autre côté répond deux fois ? ». Sa racine : le réseau n'est pas fiable — un message peut se perdre, arriver en double, tarder. « Exactly-once » est un mythe ; on conçoit pour « at-least-once ». Deux réflexes. L'idempotence rend le rejeu inoffensif : une clé pour que le serveur n'exécute la même demande qu'une fois. La saga remplace la transaction unique impossible : des étapes locales, chacune dotée d'une compensation, rejouées à rebours quand une étape tardive échoue. La règle : conçois pour « au moins une fois » — idempotence partout, réparation par compensation, jamais par verrou distribué.Issues #1 and #2 left a debt: 'what if the other side answers twice?'. Its root: the network isn't reliable — a message can be lost, arrive twice, lag. 'Exactly-once' is a myth; you design for 'at-least-once'. Two reflexes. Idempotency makes the replay harmless: a key so the server runs the same request only once. The saga replaces the impossible single transaction: local steps, each with a compensation, replayed backward when a late step fails. The rule: design for 'at least once' — idempotency everywhere, repair by compensation, never by distributed lock.
Le dernier volume s'ouvre par le pattern le plus rentable, et le plus piégeux. « Mets un cache » divise les temps de réponse par cent — mais copier une donnée, c'est en avoir deux exemplaires à garder d'accord, et sans invalidation le cache ment. La règle du volume : on ne cache pas une intuition, on cache un point chaud mesuré — et on décide comment l'invalider avant même qu'il existe.The final volume opens with the most profitable pattern, and the most treacherous. 'Add a cache' divides response times by a hundred — but copying data means two copies to keep in agreement, and without invalidation the cache lies. The volume's rule: you don't cache a hunch, you cache a measured hot path — and decide how to invalidate it before it even exists.
Quand la donnée ne tient plus sur une machine, on la découpe en shards répartis sur plusieurs nœuds. La promesse : une capacité sans plafond. Le piège : la clé de partition décide pour toujours quelles requêtes restent rapides — celles qui en sortent se dispersent sur toutes les machines (scatter-gather), et les transactions se brisent. La règle : on ne shard qu'un plafond mesuré, et on choisit la clé d'après ce qu'on lit le plus — car on ne pourra plus la changer sans tout migrer.When data no longer fits on one machine, you split it into shards spread across several nodes. The promise: capacity with no ceiling. The trap: the partition key decides forever which queries stay fast — those that step off it scatter across every machine (scatter-gather), and transactions break. The rule: you shard only a measured ceiling, and choose the key by what you read most — because you can no longer change it without migrating everything.
Dès qu'une donnée est répliquée, une question devient inévitable : que se passe-t-il quand le réseau coupe entre les copies ? Le théorème CAP répond par un mur — sous partition, on ne peut être à la fois cohérent et disponible. Le « CA » n'existe pas : il faut choisir, par donnée, entre refuser de répondre (CP) et répondre peut-être faux (AP). Numéro final de la série, sur sa thèse de bout en bout : il n'existe pas de geste gratuit.Once data is replicated, a question becomes inescapable: what happens when the network cuts between the copies? The CAP theorem answers with a wall — under a partition, you can't be both consistent and available. 'CA' doesn't exist: you must choose, per datum, between refusing to answer (CP) and answering maybe-wrong (AP). The series' final issue, on its end-to-end thesis: there's no free gesture.
Chaque numéro dissèque un seul choix de conception : le problème qui le force, son prix, et quand ne pas l'employer. Pas un catalogue de patterns à cocher — une tension comprise jusqu'à l'os, puis les patterns voisins qu'elle débloque, seulement nommés. Le schéma de dépendances animé prouve l'idée, la pagination force la concision, l'édito assume le point de vue.Each issue dissects a single design choice: the problem that forces it, its cost, and when not to use it. Not a catalog of patterns to tick off — one tension understood to the bone, then the neighboring patterns it unlocks, only named. The animated dependency diagram proves the idea, pagination forces concision, the editorial owns the point of view.
La règle tacite : aucun numéro publié sans sa phrase-réflexe et son contre-exemple. Collectionner les patterns entraîne la mémoire ; comprendre un tradeoff entraîne le jugement. Un pattern plaqué sans son « pourquoi » est une superstition qui s'effondre dès que le système change.The unspoken rule: no issue published without its one-line reflex and its counter-example. Hoarding patterns trains memory; understanding a tradeoff trains judgment. A pattern slapped on without its 'why' is a superstition that collapses the moment the system changes.