Vertical Slice & Modular Monolith : le sweet spot

14 octobre 2025

Il existe mille façons de construire une API .NET. Pourtant, au fond, tout se résume souvent à une seule question :
quelle dette technique suis-je prêt à accepter pour garder de la vitesse, sans sacrifier la qualité ?

Cet article ouvre une série consacrée à l’architecture logicielle moderne dans l’écosystème .NET.
Aujourd’hui, on parle macro design : les grandes formes d’architecture qui structurent ton application avant même la première ligne de code.

Le monolithe n’est pas ton ennemi

Quand on parle “architecture”, on a souvent le réflexe de penser microservices. C’est souvent une erreur.

Un monolithe bien structuré reste une excellente base pour la majorité des projets.
Moins de complexité, moins de coûts d’infrastructure, et une vitesse de développement bien plus élevée tant que l’équipe reste concentrée.

Ce qui tue un monolithe, ce n’est pas sa nature “mono”, c’est son couplage.
Si toutes tes couches sont imbriquées et dépendantes les unes des autres, tu es condamné à souffrir à chaque évolution.

Modular Monolith : la maturité sans la douleur

Le modular monolith est, à mon sens, le meilleur point d’équilibre pour 80 % des applications d’entreprise.

L’idée :
👉 garder un seul exécutable, mais
👉 organiser ton code en modules fonctionnels indépendants.

Chaque module représente une feature ou un bounded context (dans le vocabulaire DDD), avec son propre domaine, ses cas d’usage et ses endpoints.

Un exemple d’arborescence classique en .NET :

Et dans ton Program.cs, chaque module peut s’enregistrer comme un “plugin” autonome :

Tu gardes la simplicité du monolithe, mais tu poses les bases d’une architecture modulaire et découplée — prête à évoluer vers du microservice si nécessaire.

Clean / Onion / Hexagonal : même combat, autre perspective

Les noms changent, les principes restent.

L’idée clé de ces architectures, c’est l’inversion des dépendances :
le domaine ne dépend de rien d’autre.

Tu structures ton code autour de trois zones :

  1. Domain → le cœur du métier (entités, value objects, règles)
  2. Application → les cas d’usage (commands, queries)
  3. Infrastructure → le “monde extérieur” (DB, bus, API externes)

Dans une solution .NET, ça donne souvent :

Et dans le code :

Le domaine ne connaît jamais EF Core ni SQL.
C’est ton infrastructure qui implémente IInvoiceRepository, pas ton domaine qui s’y adapte.

C’est ce qu’on appelle “Dependency Inversion Principle” — la base du Clean.

CQRS : quand séparer, c’est gagner

Command Query Responsibility Segregation (CQRS) consiste à séparer les chemins de lecture et d’écriture.
Autrement dit :

  • les commands modifient l’état,
  • les queries le lisent, avec potentiellement un modèle optimisé.

Pourquoi ?
Parce qu’à mesure que ton système grossit, les besoins ne sont pas les mêmes :

  • d’un côté tu veux de la cohérence transactionnelle,
  • de l’autre, de la vitesse de lecture.

Avec MediatR, ça devient très naturel :

Deux handlers, deux responsabilités claires.
Et si plus tard tu veux brancher un cache Redis ou une base de lecture dédiée ?
Tu peux le faire sans rien casser.

CQRS n’est pas une religion. C’est un outil de scalabilité organisationnelle autant que technique.

Event-driven : le code qui parle par messages

Dès que tu veux découpler tes modules ou microservices, la communication par événements devient une évidence.

Le pattern “publish / subscribe” permet à ton système de réagir sans dépendance directe :

Un autre module (par ex. Notifications) peut alors s’abonner :

Là, tu tiens la base du Domain Events pattern et de l’architecture événementielle.
C’est une manière élégante de faire communiquer des modules autonomes, que ce soit dans un monolithe ou un bus distribué.

Vertical Slice Architecture : penser par feature, pas par couche

Ce pattern a beaucoup de succès dans le monde .NET depuis l’arrivée des Minimal APIs.

L’idée :
plutôt que de regrouper ton code par “Controllers / Services / Repositories”, tu regroupes tout ce qui concerne une feature dans un seul dossier.

Exemple :

Chaque slice est indépendante, testable et facilement supprimable.
C’est le pattern idéal pour des APIs légères ou des MVPs évolutifs.

Ce qu’il faut retenir

  • Ne te précipite pas vers les microservices : structure d’abord ton monolithe.
  • Le découplage dépendances → domaine est ton meilleur ami.
  • CQRS et les événements ne sont utiles que s’ils simplifient, pas s’ils complexifient.
  • L’architecture modulaire te prépare à grandir, sans réécrire.

Voici le repo du sample qui accompagne l’article :
👉 xraboteu/CleanArchitectureSample

Il montre un modular monolith .NET 9 avec Minimal APIs + MediatR, deux modules (Billing, Catalog), et une infra InMemory pour démarrer vite.
Pour tester en local :

git clone https://github.com/xraboteu/CleanArchitectureSample
cd CleanArchitectureSample
dotnet restore
dotnet run --project src/Host/Host.csproj

Prochain article : “Côté API” — comment concevoir une surface HTTP claire, maintenable et agréable à consommer, avec Minimal APIs, Carter et FluentValidation.