Ce qu'est l'architecture (et ce qu'elle n'est pas)
"L'architecture est l'ensemble des décisions coûteuses à modifier ultérieurement." - Ralph Johnson
L'architecture n'est pas "quel framework avons-nous utilisé" - c'est un détail d'implémentation. L'architecture est la forme que vous ressentez en naviguant dans la base de code : comment les modules sont séparés, dans quelle direction les dépendances circulent, quel code peut appeler quel autre.
Une bonne architecture indique à un développeur où écrire la nouvelle fonctionnalité sans ambiguïté. Une mauvaise architecture transforme cette question en débat à chaque fois.
Le coût des décisions
Le coût réel d'une décision architecturale apparaît non pas au moment où elle est prise, mais à mesure que le nombre de décisions en aval augmente. Trois mesures :
- Temps d'annulation - combien de semaines pour annuler ?
- Surface de propagation - combien de fichiers / modules dépendent de cette décision ?
- Charge cognitive - combien d'heures un nouveau développeur a-t-il besoin pour la comprendre ?
Avant de figer une décision, demandez-vous : si je réalise dans trois mois que c'est une erreur, à quel point l'annulation sera-t-elle douloureuse ?
Intention et mise en œuvre
L'architecture est l'ensemble des décisions que vous aviez l'intention de prendre ; la base de code est ce qui a réellement été construit. L'écart entre les deux est la dérive architecturale - et elle grandit en silence jusqu'à ce qu'un changement censé prendre une heure en prenne une semaine.
Le travail n'est pas de dessiner une fois le diagramme parfait. C'est de garder l'intention et la mise en œuvre assez proches pour que le diagramme prédise encore le comportement du code.
En couches (n-tiers)
La séparation classique : présentation → application → domaine → données. Chaque couche ne peut appeler que celle du dessous. Le modèle le plus facile à enseigner et le plus facile à enfreindre - un appel de l'UI directement vers la couche de données, et le découpage en couches n'est qu'une fiction.
À utiliser quand l'équipe est petite et le domaine peu profond. Sa faiblesse : la logique métier a tendance à fuir vers le haut dans les contrôleurs et vers le bas dans la base de données.
Hexagonale · Ports et adaptateurs
Le domaine est au centre et ne sait rien du monde extérieur. Tout ce qui est externe - base de données, HTTP, file de messages - lui parle via des ports (interfaces) que des adaptateurs implémentent.
Le gain : testabilité et remplaçabilité. Vous pouvez remplacer Postgres par un faux en mémoire dans les tests, ou REST par gRPC, sans toucher une ligne de code du domaine.
Clean Architecture
Un raffinement de l'hexagonale : des anneaux concentriques où les dépendances ne pointent que vers l'intérieur, vers les entités du domaine. Frameworks, UI et bases de données vivent sur l'anneau le plus externe - des détails, pas le cœur.
Il vous offre un domaine qui se compile et se teste sans aucune infrastructure. Le coût, c'est la cérémonie - plus d'interfaces, plus de mapping. Justifié pour les systèmes de longue durée, excessif pour un panneau d'administration CRUD.
DDD - Conception pilotée par le domaine
Le DDD n'est pas un framework - c'est une discipline de vocabulaire. "Aggregate", "bounded context", "anti-corruption layer" - un langage commun pour parler du problème.
Quand utiliser le DDD ?
- Le domaine est complexe, souvent confondu avec la complexité technique.
- Plusieurs équipes travaillent sur le même domaine.
- Le métier et l'ingénierie parlent des langages différents.
Le DDD n'est pas pour tous les projets. S'il semble trop lourd pour un panneau d'administration axé CRUD, c'est probablement le cas.
Big Ball of Mud
Aucune architecture discernable : tout dépend de tout, l'état est partagé globalement, et la seule façon de comprendre un changement est de l'exécuter. La plupart des systèmes y arrivent non par décision mais par mille échéances.
On ne réécrit pas une boule de boue - on l'étrangle. Encapsulez une couture, déplacez une capacité derrière une interface propre, recommencez. La carte des dépendances d'AppArch vous montre où sont déjà les coutures.
Antipattern : module dieu
Un "module dieu" est celui appelé de partout et qui contient tout. Des noms comme Utils, Common, Shared sont des signaux d'alerte.
# une règle qui le détecte - id: GOD-001 max_fan_in: 25 message: "Module imported by >25 files - split or rename"
Solution : diviser en gros en trois. Le groupe un est la vraie infra (logger, config). Le groupe deux est les primitives de domaine (money, id). Le groupe trois est probablement un module distinct qui s'y cache.
Dépendances circulaires
Le module A a besoin de B, B a besoin de A. Désormais aucun ne peut être compris, testé ou déployé seul, et l'ordre de build devient un pile ou face. Les cycles sont le prédicteur le plus fiable d'un module que personne ne veut toucher.
Brisez le cycle en extrayant le concept partagé dans un troisième module dont les deux dépendent, ou en inversant une direction avec une interface. AppArch signale les cycles dès qu'ils apparaissent dans une merge request.
Abstractions qui fuient
Une abstraction fuit quand il faut savoir comment elle est implémentée pour l'utiliser correctement - un repository qui lève des exceptions SQL, un cache qui force les appelants à gérer la péremption. Elle promettait de cacher un détail et ne l'a pas fait.
Soit colmatez la fuite - traduisez les erreurs d'implémentation à la frontière - soit abandonnez l'abstraction. Une abstraction qui fuit est pire que pas d'abstraction : vous payez l'indirection sans obtenir l'isolation.
Revue d'architecture
Une revue n'est pas une barrière qui bloque les merges ; c'est une conversation sur le fait qu'un changement rapproche ou éloigne le système de sa forme voulue. Les meilleures revues demandent « qu'est-ce que cela rendra difficile plus tard ? » plutôt que « est-ce correct maintenant ? »
Gardez-la légère et continue. Une courte revue sur chaque merge request importante vaut mieux qu'un comité d'architecture trimestriel que tout le monde contourne.
Architecture Decision Records
Un ADR capture une décision : le contexte, les options envisagées, le choix et ses conséquences - un paragraphe ou deux, versionné à côté du code. Six mois plus tard, il répond à la seule question qui compte : pourquoi est-ce ainsi ?
Rédigez l'ADR au moment de la décision, pas après. Remplacez plutôt que supprimer - la trace de pourquoi vous avez changé d'avis vaut autant que la réponse actuelle.
Refactorisation à grande échelle
Une grosse refactorisation livrée en un seul merge géant est une réécriture déguisée - et elle est annulée dès la première casse. La vraie refactorisation est une suite de petits pas préservant le comportement, chacun livrable seul.
Ajoutez le nouveau chemin à côté de l'ancien, migrez les appelants progressivement, puis supprimez l'ancien chemin. Les tests sont la corde de sécurité ; sans eux, vous ne refactorisez pas, vous pariez.