Vertical Slices vs Clean, ou le long de quel axe ça changeVertical Slices vs Clean, or which axis change runs along
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.
AudienceAudience
Dev qui hésite entre Clean strict et découpage par featureDev torn between strict Clean and feature-based cutting
Le même code, deux découpes. Le long de quel axe ton code change-t-il ?The same code, two cuts. Along which axis does your code change?
Le Vol 1 #2 a tranché le sens des dépendances ; le #1 de ce volume a réparti le savoir entre couches. Reste une question qu'aucun des deux ne pose : selon quel axe ranger les dossiers ? Par rôle technique — controllers/, services/, repositories/ — ou par feature — checkout/, refund/, inventory/, chacune portant sa propre tranche verticale. Ce n'est pas une querelle de goût : on optimise la découpe pour le changement qu'on fait le plus souvent.Vol 1 #2 settled the direction of dependencies; this volume's #1 distributed knowledge across layers. One question neither asks remains: along which axis do you arrange the folders? By technical role — controllers/, services/, repositories/ — or by feature — checkout/, refund/, inventory/, each carrying its own vertical slice. It's not a matter of taste: you optimize the cut for the change you make most often.
Deux arborescences pour la même applicationTwo file trees for the same application
# Par couche (horizontal) # Par feature (vertical)
controllers/ checkout/
checkout_controller controller · service · repo
refund_controller refund/
services/ controller · service · repo
checkout_service inventory/
refund_service controller · service · repo
repositories/
order_repository
Deux axes, un seul peut être « facile »Two axes, only one can be 'cheap'
Par coucheBy layer
changer une techno · 1 dossierswap a tech · 1 folder
vs
Par featureBy feature
changer un comportement · 1 dossierchange a behavior · 1 folder
Ranger par couche rend le changement de techno local et le changement de feature dispersé. Ranger par feature fait l'inverse. Il n'y a pas de découpe gratuite : on choisit l'axe qu'on rend bon marché.Arranging by layer makes a tech swap local and a feature change scattered. Arranging by feature does the reverse. There's no free cut: you choose which axis you make cheap.
Le réflexe du volumeThe volume's reflex
« Cette couche connaît-elle des choses qu'elle ne devrait pas ? » se prolonge ici en : « mes frontières suivent-elles l'axe selon lequel le code change vraiment ? ». Une découpe horizontale parfaite n'aide en rien si chaque demande métier traverse les trois couches d'un coup.'Does this layer know things it shouldn't?' extends here into: 'do my boundaries follow the axis along which the code actually changes?'. A perfect horizontal split helps nothing if every business request crosses all three layers at once.
Ce n'est pas un XOR.It's not a XOR.
Vertical Slices et Clean ne s'excluent pas — on les oppose pour les comprendre, on les marie en pratique. La réponse mûre, qu'on construit jusqu'au bilan : des slices au sommet, et la règle de dépendance vers l'intérieur (Vol 1 #2) à l'intérieur de chaque slice qui porte un vrai métier.Vertical Slices and Clean don't exclude each other — we oppose them to understand them, we marry them in practice. The mature answer, built up to the wrap-up: slices on top, and the inward dependency rule (Vol 1 #2) inside each slice that carries real business.
№ 02La douleurThe pain4 min
Ajouter une feature touche six fichiers dans trois couches — à chaque fois.Adding a feature touches six files across three layers — every time.
Le découpage horizontal est excellent pour ce qui change rarement : remplacer Postgres, changer de framework web. Mais ce n'est presque jamais ce qui change. Ce qui change, c'est une feature : « emballer un cadeau au checkout », « rembourser partiellement ». Et une feature, dans une arborescence par couche, est éparpillée sur tous les étages. On rouvre les trois couches ensemble, toujours.Horizontal splitting is excellent for what rarely changes: replacing Postgres, swapping the web framework. But that's almost never what changes. What changes is a feature: 'gift-wrap at checkout', 'partial refund'. And a feature, in a by-layer tree, is scattered across every floor. You reopen all three layers together, always.
№ 1 · Le diff part en éventailThe diff shotgun-spreads
« Ajouter l'emballage cadeau » modifie le contrôleur, le service, le repository, le DTO, la migration. Cinq fichiers dans cinq dossiers pour un seul changement métier — la revue de code doit recoller l'intention dispersée.'Add gift wrapping' edits the controller, the service, the repository, the DTO, the migration. Five files in five folders for one business change — the reviewer must reassemble the scattered intent.
№ 2 · La couche partagée devient un dieuThe shared layer becomes a god
Toutes les features dépendent du même OrderService et du même repository. Ils gonflent, se couplent à tout, et toucher une feature risque d'en casser une autre — le couplage est revenu par la couche commune.Every feature depends on the same OrderService and the same repository. They bloat, couple to everything, and touching one feature risks breaking another — coupling came back through the shared layer.
№ 3 · L'arbo crie le frameworkThe tree screams the framework
Ouvrir le projet montre controllers/, services/, repositories/ : on apprend qu'il y a un framework web, pas que l'app fait de la réservation. La structure ne dit rien du métier — elle dit la techno.Opening the project shows controllers/, services/, repositories/: you learn there's a web framework, not that the app does bookings. The structure says nothing about the business — it says the tech.
Un seul changement métier, éparpilléOne business change, scattered
Le détecteur : « quand je fais le changement le plus fréquent, combien de dossiers de premier niveau dois-je ouvrir ? ». Si la réponse suit l'axe technique alors que le changement suit l'axe métier, les frontières sont perpendiculaires au flux du travail. Le réflexe : faire coïncider l'axe de découpe avec l'axe de variation (Vol 0 #1) — ici, la feature.The detector: 'when I make the most frequent change, how many top-level folders must I open?'. If the answer follows the technical axis while the change follows the business axis, the boundaries are perpendicular to the flow of work. The reflex: align the cut's axis with the axis of variation (Vol 0 #1) — here, the feature.
№ 03L'axe du changementThe axis of change5 min
Aucune découpe n'est gratuite : on choisit l'axe qu'on rend bon marché.No cut is free: you choose which axis you make cheap.
Voici la même application comme une grille : trois couches en lignes, trois features en colonnes. Un changement allume toujours soit une colonne (une feature, tous ses étages), soit une ligne (une techno, toutes les features). Ce qui varie, c'est le sens où l'on a tracé les dossiers. L'animation montre le même changement se localiser dans un cas et s'éparpiller dans l'autre — preuve qu'on ne gagne pas sur les deux axes.Here's the same application as a grid: three layers as rows, three features as columns. A change always lights up either a column (one feature, all its floors) or a row (one tech, all features). What varies is the direction you drew the folders. The animation shows the same change localize in one case and scatter in the other — proof you can't win on both axes.
Animation — la grille couche × featureAnimation — the layer × feature grid
Par couche · on change une featureBy layer · changing a feature · 1/4
La feature touche les trois étages — mais les dossiers sont les couches. Le diff s'éparpille sur trois dossiers.The feature touches all three floors — but the folders are the layers. The diff scatters across three folders.
Une slice verticale, repliée sur elle-même — 2 lignes à retrouverA vertical slice, self-contained — 2 lines to recall
checkout/ # la feature possède toute sa tranche
Endpoint.handle(req) # entrée CheckoutStore # port + adapter, locaux à la slice# changer le checkout = ouvrir un dossier, et un seul.# le métier qui mérite la règle de dépendance (Vol 1 #2)
2 ligne(s) à retrouver — écris-la de tête avant de cliquer2 line(s) to recall — write it in your head before clicking
Le motif transférable.The transferable pattern.
« Aligne tes frontières de dossiers sur l'axe selon lequel le code change le plus. » C'est l'axe de variation du Vol 0 #1 appliqué à l'arborescence elle-même. Comme presque tout changement est une feature et presque aucun n'est un remplacement de base, le découpage vertical gagne le cas courant — au prix des changements transverses, plus rares, qu'il faut alors discipliner.'Align your folder boundaries with the axis along which code changes most.' It's Vol 0 #1's axis of variation applied to the file tree itself. Since almost every change is a feature and almost none is a database swap, the vertical cut wins the common case — at the cost of cross-cutting changes, rarer, which must then be disciplined.
Modèle mentalMental model
L'imageThe image
Une feuille de papier qu'on plie. Clean dit dans quel sens pointent les flèches dessinées dessus — vers l'intérieur. Vertical Slices dit le long de quelle ligne on plie la feuille — par feature. Deux gestes sur le même papier, pas deux papiers rivaux.A sheet of paper you fold. Clean says which way the arrows drawn on it point — inward. Vertical Slices says along which line you fold the sheet — by feature. Two gestures on the same paper, not two rival sheets.
La règleThe rule
Aligne la frontière des dossiers sur l'axe où le code change le plus. Presque tout changement est une feature → découpe par feature ; et garde la règle de dépendance vers l'intérieur à l'intérieur des slices qui portent un vrai métier.Align folder boundaries with the axis where code changes most. Almost every change is a feature → cut by feature; and keep the inward dependency rule inside the slices that carry real business.
Où l'analogie casseWhere the analogy breaks
Le pli n'est pas un choix unique et définitif, et c'est le contresens le plus courant. Les deux axes sont orthogonaux : on plie par feature au sommet ET on oriente les flèches vers l'intérieur dans les slices qui le méritent. Le vrai piège n'est pas de mal choisir son camp, c'est de croire qu'une découpe est gratuite — optimiser un axe renchérit toujours l'autre. Par feature rend le changement de techno transverse ; par couche rend le changement de feature dispersé.The fold isn't a single, final choice, and that's the most common misreading. The two axes are orthogonal: you fold by feature on top AND point arrows inward in the slices that deserve it. The real trap isn't picking the wrong camp, it's believing a cut is free — optimizing one axis always taxes the other. By feature makes a tech swap cross-cutting; by layer makes a feature change scattered.
№ 04Le piègeThe trap4 min
Découper en features n'abolit pas la règle de dépendance — ça la déplace.Cutting by feature doesn't abolish the dependency rule — it relocates it.
Le piège des slices verticales n'est pas leur idée, c'est leur mise en œuvre naïve : croire que « par feature » dispense de toute abstraction partagée. On copie alors la règle métier dans chaque slice, ou pire, une slice va fouiller dans les internes d'une autre. Le couplage horizontal qu'on fuyait revient — déguisé en indépendance. Bascule le toggle : suis la règle de remise et les imports entre slices.The vertical-slice trap isn't the idea, it's the naive build: believing 'by feature' frees you from any shared abstraction. So you copy the business rule into each slice, or worse, one slice digs into another's internals. The horizontal coupling you fled comes back — disguised as independence. Flip the toggle: follow the discount rule and the cross-slice imports.
# slices isolées ; ce qui est vraiment commun vit dans un noyau possédé
shared/pricing/ # noyau de domaine, contrat explicite
discount_for(customer, total) -> Money
checkout/ # importe le noyau, n'expose rien
ConfirmCheckout: total = discount_for(customer, subtotal)
refund/ # importe le MÊME noyau
IssueRefund: amount = discount_for(customer, original)
# une slice n'atteint jamais les internes d'une autre.
Pourquoi c'est justeWhy it's right
Ce qui est réellement commun — calculer une remise — vit dans un noyau possédé, avec un contrat explicite ; les slices l'importent sans jamais s'atteindre l'une l'autre. Le seuil change ? Un seul fichier dans shared/pricing/. Chaque slice reste autonome, et la slice qui porte un vrai métier garde, à l'intérieur d'elle-même, la règle de dépendance vers l'intérieur du Vol 1 #2.What's genuinely common — computing a discount — lives in an owned kernel with an explicit contract; slices import it without ever reaching into each other. The threshold changes? A single file in shared/pricing/. Each slice stays autonomous, and the slice that carries real business keeps, inside itself, the inward dependency rule from Vol 1 #2.
La règle qui tranche.The rule that settles it.
Une slice peut dépendre d'un noyau partagé et explicite ; jamais des internes d'une autre slice. Le partage horizontal n'est pas interdit — il est ramené à ce qui est vraiment commun, et nommé. Vertical Slices ne supprime pas les frontières du Vol 1 : il en change la maille. La règle de dépendance vit maintenant à l'intérieur de chaque slice, pas en travers de toute l'application.A slice may depend on an explicit, shared kernel; never on another slice's internals. Horizontal sharing isn't forbidden — it's narrowed to what's truly common, and named. Vertical Slices doesn't remove Vol 1's boundaries: it changes their grain. The dependency rule now lives inside each slice, not across the whole application.
Combien partager ?How much to share?
La tension jumelle du Vol 0 #2 : un noyau partagé trop gros redevient la couche-dieu qu'on fuyait ; trop maigre, et la règle se duplique. Le réglage : ne mutualiser qu'un savoir réellement identique entre slices (la formule de remise), jamais une ressemblance de surface. Préférer un peu de duplication à une mauvaise abstraction tant que la règle n'a pas prouvé qu'elle est la même.The twin tension of Vol 0 #2: a shared kernel too large becomes the god-layer you fled; too thin, and the rule duplicates. The tuning: pool only knowledge genuinely identical across slices (the discount formula), never a surface resemblance. Prefer a little duplication to the wrong abstraction until the rule has proven it's the same.
№ 05Bilan & éditoWrap-up & editorial2 min
Découper par couche, par feature — ou les deux, à la bonne maille.Cut by layer, by feature — or both, at the right grain.
Les deux axes, côte à côteThe two axes, side by side
Par couche (Clean)By layer (Clean)
Par feature (Slices)By feature (Slices)
Changer une featureChange a feature
éparpillé sur 3 couchesscattered over 3 layers
un dossierone folder
Changer la technoSwap the tech
un dossier (la couche)one folder (the layer)
une cellule par sliceone cell per slice
L'arbo dit…The tree says…
le frameworkthe framework
le métier (Screaming)the business (Screaming)
Le risqueThe risk
la couche partagée devient un dieuthe shared layer becomes a god
le monolithe distribué de dossiersthe distributed monolith of folders
Idée cléKey idea
optimise le changement de technooptimizes the tech swap
optimise le changement de featureoptimizes the feature change
Le contre-procès : quand Clean est un sur-coûtThe counter-trial: when Clean is overkill
La Clean / Hexagonal du Vol 1 #2 a un loyer réel — ports des deux côtés, composition root, couche de traduction. On ne le paie pas par principe. On reste plat — couches simples, voire framework direct — quand :The Clean / Hexagonal of Vol 1 #2 has real rent — ports on both sides, a composition root, a translation layer. You don't pay it on principle. Stay flat — simple layers, even framework-direct — when:
le produit est un CRUD : la plupart des slices n'ont aucune règle. Une slice plate — endpoint → requête — est honnête ; l'hexagone n'y entourerait que du vide. On réserve la règle de dépendance aux rares slices qui portent un invariant.the product is CRUD: most slices have no rules. A flat slice — endpoint → query — is honest; the hexagon there would wrap only emptiness. Reserve the dependency rule for the rare slices carrying an invariant.
l'équipe tient en deux personnes et le time-to-market prime : l'indirection coûte aujourd'hui et ne rapporte qu'au changement futur. C'est le YAGNI du Vol 0 #3 — tant que le point de non-retour n'est pas en vue, on garde le code simple à supprimer.the team is two people and time-to-market wins: indirection costs today and only pays on a future change. It's Vol 0 #3's YAGNI — until the point of no return is in sight, keep code easy to delete.
le métier n'est pas encore compris : tracer des frontières sur un domaine qu'on découvre, c'est figer de mauvaises lignes. On laisse le code grumeleux jusqu'à voir l'axe de variation réel (Vol 0 #1), puis on découpe.the business isn't understood yet: drawing boundaries on a domain you're still discovering freezes the wrong lines. Keep the code lumpy until the real axis of variation shows (Vol 0 #1), then cut.
Le réflexe n'est donc ni « Clean partout » ni « slices partout ». C'est : « slices au premier niveau — l'arbo doit crier le métier — et, à l'intérieur, la règle de dépendance seulement pour les slices qui ont un vrai métier à protéger. Les autres restent plates. »So the reflex is neither 'Clean everywhere' nor 'slices everywhere'. It's: 'slices at the top level — the tree should scream the business — and inside, the dependency rule only for slices that have real business to protect. The rest stay flat.'
Mot de l'éditeurFrom the editor
On présente souvent Vertical Slices et Clean comme deux camps. Ce sont deux axes d'une même feuille de papier : Clean dit dans quel sens pointent les dépendances, Slices dit le long de quelle ligne on plie. Choisir un camp, c'est confondre l'orientation et la maille. Le débutant cherche LA bonne architecture ; l'architecte aligne chaque frontière sur l'axe selon lequel ce système-là changera.Vertical Slices and Clean are often pitched as two camps. They're two axes of the same sheet of paper: Clean says which way dependencies point, Slices says along which line you fold. Picking a camp confuses orientation with grain. The beginner hunts for THE right architecture; the architect aligns each boundary with the axis along which this particular system will change.
Deux numéros, deux réponses à la même question — qui a le droit de connaître quoi. Une couche ne doit savoir que son métier (#1), et la frontière des dossiers doit suivre l'axe du changement (#2). Mais découper en slices laisse un reste embarrassant : l'auth, le log, la validation, la trace ne sont d'aucune slice — ils les traversent toutes. Les dupliquer dans chacune, c'est signer l'oubli du jour où l'une manquera à l'appel ; les remonter dans le cœur, c'est refaire la couche qui en sait trop. Le #3 sort ce transverse du cœur sans l'éparpiller.Two issues, two answers to the same question — who has the right to know what. A layer should know only its job (#1), and folder boundaries should follow the axis of change (#2). But cutting into slices leaves an awkward remainder: auth, logging, validation, tracing belong to no slice — they cross all of them. Duplicating them in each is signing up for the day one goes missing; hoisting them into the core is rebuilding the layer that knows too much. #3 pulls that cross-cutting concern out of the core without scattering it.
Les voisins que ce numéro débloqueThe neighbors this issue unlocks
Screaming Architecture — le test de l'arbo : les dossiers de premier niveau doivent nommer le métier (checkout, refund), pas la techno (controllers, services). Si l'arbo crie le framework, la découpe suit le mauvais axe.the tree test: top-level folders should name the business (checkout, refund), not the tech (controllers, services). If the tree screams the framework, the cut follows the wrong axis.
Feature Folders — la forme la plus simple du découpage vertical : un dossier par cas d'usage, sans cérémonie. Le point d'entrée des slices avant d'y porter la règle de dépendance.the simplest form of vertical cutting: one folder per use case, no ceremony. The entry point to slices before carrying the dependency rule into them.
Modular Monolith — la slice poussée jusqu'au module à frontière dure — antichambre des microservices, sans le coût réseau. On y revient quand on parlera de découpage en services.the slice pushed to a hard-boundaried module — the antechamber of microservices, without the network cost. More when we cover splitting into services.
Le kata de décision — tranche avant de déroulerThe decision kata — decide before you scroll
Pas de code à faire passer au vert ici : ce numéro est un choix d'axe, pas un mécanisme. Le kata est donc un scénario à trancher. Lis-le, engage-toi sur chaque décision — à voix haute ou par écrit — avant d'ouvrir la réponse. Se tromper avant de lire ancre mieux que lire d'abord.No code to turn green here: this issue is a choice of axis, not a mechanism. So the kata is a scenario to settle. Read it, commit to each decision — out loud or in writing — before opening the answer. Getting it wrong before reading anchors better than reading first.
Le scénarioThe scenario
Une équipe de trois sur un SaaS de facturation, 18 mois de vie. L'arborescence est en controllers/ services/ repositories/. Les six derniers mois, en chiffres : 90 % des tickets sont « ajouter ou modifier une feature » — chacun touche aujourd'hui les trois dossiers ; un seul chantier techno (passage de Postgres à Aurora) ; et deux features, invoicing et dunning, partagent le calcul des pénalités de retard, aujourd'hui recopié dans les deux.A team of three on a billing SaaS, 18 months old. The tree is controllers/ services/ repositories/. The last six months, in numbers: 90% of tickets are 'add or change a feature' — each touches all three folders today; a single tech project (moving Postgres to Aurora); and two features, invoicing and dunning, share the late-penalty calculation, currently copied into both.
Chez toi, cette semaine.In your codebase, this week.
Ouvre ton arborescence de premier niveau. Crie-t-elle le métier (checkout/, billing/) ou le framework (controllers/, services/) ? Test rapide sur l'axe réel du changement : git log --format= --name-only -n 300 | grep -oE '^[^/]+/' | sort | uniq -c | sort -rn — les dossiers les plus souvent touchés ensemble devraient être une seule frontière. S'ils s'éparpillent sur controllers/services/repos à chaque feature, ta découpe suit le mauvais axe : celui qui change le moins.Open your top-level tree. Does it scream the business (checkout/, billing/) or the framework (controllers/, services/)? A quick test on the real axis of change: git log --format= --name-only -n 300 | grep -oE '^[^/]+/' | sort | uniq -c | sort -rn — the folders most often touched together should be a single boundary. If they scatter across controllers/services/repos on every feature, your cut follows the wrong axis: the one that changes least.