Concevoir par les principes structurels¶
Chapter Info
Calculating... Writing Progress: 80%
Une bonne conception logicielle a besoin d’une structure qui facilite la gestion du changement. Separation of Concerns, Single Responsibility, Open-Closed et Dependency Inversion offrent quatre façons complémentaires d’organiser un système. Ensemble, ils aident à définir des frontières claires, attribuer les responsabilités, permettre l’extension et maîtriser les dépendances.
Introduction¶
La structure sert à contenir le changement¶
Un système peut fonctionner parfaitement aujourd’hui et être pourtant mal conçu pour demain. Le vrai test arrive quand quelque chose change. Dans un système mal structuré, même un petit changement peut avoir des conséquences inattendues sur des parties sans rapport. Dans un système bien structuré, les changements restent plus faciles à comprendre, à gérer et à contenir. Une bonne structure limite l’impact du changement.
Séparer ce qui change pour des raisons différentes¶
Tout dans un système ne change pas pour la même raison ni au même rythme. Quand des préoccupations sans rapport sont mélangées, modifier l’une peut affecter les autres de façon inattendue. Une bonne conception reconnaît ces différences et évite qu’elles ne s’enchevêtrent inutilement. Ce qui change pour des raisons différentes doit pouvoir évoluer indépendamment.
Donner à chaque responsabilité un propriétaire clair¶
Un système devient difficile à maintenir quand les responsabilités sont floues ou éparpillées entre plusieurs composants. Quand chaque responsabilité a une place claire, il devient plus facile de savoir où doivent se faire les changements et ce qu’ils peuvent affecter. Des responsabilités claires rendent le système plus facile à comprendre et à faire évoluer.
Étendre sans casser ce qui fonctionne¶
Le logiciel doit évoluer à mesure que de nouveaux besoins émergent. Mais ajouter des fonctionnalités ne doit pas mettre sans cesse en danger le comportement existant. Une bonne conception anticipe que les systèmes vont grandir et ménage de la place pour de nouvelles capacités sans perturber inutilement ce qui fonctionne déjà. La croissance ne doit pas se faire au détriment de la stabilité.
Dépendre de ce qui compte, pas de la manière de le faire¶
Tout dans un système n’a pas la même importance ni la même durée de vie. Certaines décisions définissent ce que le système doit accomplir, d’autres concernent la façon dont il fonctionne aujourd’hui. Une bonne conception reconnaît cette différence et empêche des choix temporaires de contrôler des comportements essentiels. Ce qui compte le plus ne doit pas être dicté par ce qui peut changer demain.
Garder les préoccupations séparées¶
Separation of Concerns (SoC)¶
La Separation of Concerns (SoC) organise un système pour que des préoccupations différentes soient prises en charge par des composants distincts. Règles métier, présentation, stockage et intégration ont des finalités différentes et évoluent pour des raisons différentes. Les garder séparés rend le système plus facile à comprendre, à modifier et à tester, tout en réduisant le risque qu’un changement dans une préoccupation affecte des parties sans rapport.
La SoC à toutes les échelles¶
La Separation of Concerns s’applique à toutes les échelles, des fonctions et classes aux modules et services. À chaque niveau, l’objectif est le même : garder ensemble les comportements liés et séparer les préoccupations qui évoluent indépendamment. Les bonnes frontières dépendent du contexte, mais la question demeure : chaque partie a-t-elle un but clair et cohérent ?
La cohésion : l’autre moitié¶
Séparer n’est utile que si ce qui reste ensemble va réellement ensemble. La cohésion est la partenaire silencieuse de la SoC : chaque unité séparée doit elle-même être étroitement centrée sur un seul but. Sans cohésion, on n’obtient pas des modules propres — on obtient un tas de fragments, chacun aussi confus que le système d’origine, en plus petit. Une bonne structure équilibre une forte cohésion au sein des composants et un faible couplage entre eux.
Les frontières ont besoin de contrats¶
Séparer les préoccupations crée des frontières, mais les frontières ont besoin de contrats clairs pour fonctionner. Un contrat définit ce que chaque composant fournit et ce que les autres peuvent en attendre. Sans contrats explicites, les composants peuvent encore dépendre d’hypothèses cachées ou de détails d’implémentation partagés. De bons contrats permettent aux composants d’évoluer indépendamment tout en préservant leur collaboration.
Chapitre associé
Concevoir pour la testabilité montre comment des contrats explicites facilitent la vérification de composants isolés et de leurs intégrations.
Donner à chaque composant une seule responsabilité¶
Des responsabilités claires façonnent l’architecture¶
En conception logicielle, une responsabilité définit le but d’un composant : ce qu’il fait, quelles décisions lui appartiennent et où s’arrêtent ses frontières. Des responsabilités claires aident les développeurs à comprendre où se place un comportement et quel composant doit traiter une exigence donnée. Une bonne architecture rend ces frontières explicites, de sorte que chaque partie du système ait un rôle bien défini.
De la séparation à la responsabilité¶
Separation of Concerns et Single Responsibility sont des principes complémentaires qui traitent d’aspects différents de la structure logicielle. La SoC demande ce qui doit être séparé. La SRP demande qui doit posséder chaque responsabilité. La SoC identifie les préoccupations qui évoluent pour des raisons différentes, tandis que la SRP garantit que chaque composant a une responsabilité claire et ciblée. Ensemble, elles créent des frontières qui gardent les changements locaux et empêchent des responsabilités sans rapport de s’emmêler.
Une seule raison de changer : le Single Responsibility Principle (SRP)¶
Dans un système bien conçu, chaque composant doit avoir une seule raison claire de changer, représentant un seul groupe de parties prenantes. C’est ce qu’on appelle le Single Responsibility Principle (SRP). Une responsabilité n’est pas simplement une opération : c’est une raison cohérente pour un composant d’évoluer. Un composant peut réaliser plusieurs opérations liées tout en servant une seule responsabilité.
Quand les responsabilités s’emmêlent¶
Quand plusieurs parties prenantes, comme les RH, la Comptabilité et l’Administration de bases de données, influencent le même composant, les changements demandés par un groupe peuvent affecter les autres sans le vouloir. Un module qui gère le traitement de la paie, les dossiers des employés et le stockage des données mélange des responsabilités qui évoluent pour des raisons différentes. Les séparer réduit les dépendances involontaires et rend les changements plus faciles à comprendre et à maintenir. Mais la séparation ne doit pas devenir fragmentation : gardez ensemble ce qui change ensemble.
Étendre sans modifier ce qui fonctionne déjà¶
Open-Closed Principle (OCP)¶
L’Open-Closed Principle (OCP) encourage des structures logicielles qui permettent d’ajouter un nouveau comportement sans modifier sans cesse le code existant et stable. Les composants doivent être ouverts à l’extension tout en restant fermés à la modification inutile. Le but n’est pas d’éliminer les changements, mais de rendre les extensions plus sûres et de réduire le risque de casser ce qui fonctionne déjà.
L’OCP dans le code¶
Dans un système de reporting, ajouter un nouveau format d’export doit signifier créer un nouvel exporter, et non ajouter une condition if format_type == ... de plus dans le cœur.
Les interfaces définissent les points d’extension¶
Une interface définit un contrat que plusieurs implémentations peuvent suivre. Par exemple, les exporters CSV, PDF et XML implémentent tous la même interface Exporter. Le système central travaille avec ce contrat sans savoir quelle implémentation est utilisée. De nouveaux formats peuvent être ajoutés sans modifier la logique existante.
L’héritage et la composition permettent l’extension¶
L’héritage permet à une classe d’étendre ou de spécialiser le comportement d’une autre classe. La composition construit des fonctionnalités en combinant des composants indépendants. Les deux peuvent soutenir l’OCP en permettant un nouveau comportement sans réécrire la logique existante. La composition est souvent plus flexible car les composants peuvent être remplacés ou combinés sans changer les hiérarchies de classes.
Les plugins : étendre sans modifier le cœur¶
À l’échelle du système, l’Open-Closed Principle peut s’appliquer via des architectures de plugins. Les plugins ajoutent de nouvelles capacités à travers des points d’extension prédéfinis, permettant au système de grandir sans modifier sans cesse son cœur. Le framework fournit les mécanismes d’intégration des extensions, tandis que chaque plugin implémente son propre comportement. De nouvelles fonctionnalités peuvent être introduites sans que le cœur soit responsable de chaque variation.
Des extensions auto-descriptives¶
À mesure que les systèmes grandissent via des plugins ou des adaptateurs, chaque extension doit décrire ses propres capacités et exigences plutôt que de forcer le framework à gérer chaque cas particulier. Le framework fournit des mécanismes communs de découverte, de validation et d’application des politiques, tandis que chaque extension définit son comportement et ses besoins spécifiques. Cela permet d’ajouter de nouvelles extensions sans modifier sans cesse le cœur.
Garder le framework générique¶
Le framework doit offrir des mécanismes génériques — découverte, validation de schéma, application des politiques — et non une liste croissante de cas particuliers. Un registre léger pour le câblage est acceptable ; embarquer au centre une logique propre à chaque extension ne l’est pas. Cela prolonge l’idée Open-Closed : le nouveau comportement est livré surtout dans le nouveau module, pas dans un orchestrateur omniscient.
Étendre pour des besoins réels, pas imaginaires¶
Les points d’extension doivent répondre à des changements réels ou prévisibles, pas à toutes les possibilités. Ajouter des interfaces et des abstractions pour des exigences hypothétiques crée une complexité inutile. Commencez par une conception simple et introduisez des points d’extension quand la variation devient visible. L’OCP vise à faciliter le changement, pas à prédire tous les changements futurs.
Garder le cœur indépendant (Dependency Inversion Principle)¶
Un système bien structuré sépare ce qui définit son comportement des technologies utilisées pour le délivrer. Les règles métier doivent rester indépendantes des bases de données, des frameworks et des services externes, afin que les choix techniques puissent évoluer sans remodeler le cœur. Le Dependency Inversion Principle offre un moyen d’établir et de préserver cette frontière.
Découpler les détails d’implémentation¶
La logique métier doit exprimer ce que le système doit accomplir, pas les étapes techniques nécessaires pour y parvenir. Les opérations métier, comme décider si une commande peut être honorée ou quand un paiement est requis, appartiennent au niveau métier, tandis que leurs implémentations techniques restent derrière des frontières claires.
Identifier le cœur et les détails¶
Pour garder le cœur indépendant, il faut d’abord distinguer le cœur des détails. Le cœur définit ce que fait le système : ses règles métier et son comportement essentiel. Un détail est quelque chose que l’on peut remplacer sans changer ces règles — une base de données, un framework ou un service externe.
Laisser le cœur piloter les détails¶
Le cœur définit le but et le comportement du système. Les décisions techniques doivent soutenir ces besoins, pas les dicter. Bases de données, frameworks et fournisseurs cloud peuvent changer, mais les règles métier essentielles doivent rester indépendantes de ces choix. Le cœur donne la direction ; les détails doivent suivre.
Laisser le cœur fixer les termes de ses dépendances¶
Le cœur doit définir ce dont il a besoin du monde extérieur, sans dépendre de technologies spécifiques. Il établit des contrats qui expriment ses exigences, tandis que bases de données, frameworks et services externes fournissent des implémentations qui les satisfont.
Pourquoi parle-t-on d’inversion des dépendances ?¶
Cette façon de structurer les dépendances s’appelle le Dependency Inversion Principle (DIP). Le nom vient de l’inversion d’une dépendance courante dans le code. Normalement, la logique métier appelle une base de données et dépend de son implémentation. Avec le DIP, la logique métier définit le contrat dont elle a besoin, et l’implémentation de la base de données dépend de ce contrat. Le flux d’exécution atteint toujours la base de données, mais la dépendance au niveau du code source pointe désormais vers le contrat défini par le cœur.
Définir les contrats autour des besoins métier¶
Un bon contrat décrit ce dont le métier a besoin, pas comment fonctionne une technologie particulière. Par exemple, un service de commandes peut avoir besoin d’envoyer une notification, sans savoir si elle sera livrée par email, SMS ou un autre fournisseur. Le contrat exprime ce besoin, tandis que chaque implémentation décide comment y répondre. Introduisez des abstractions là où elles protègent le cœur de détails susceptibles de changer, pas pour chaque dépendance.
L’injection de dépendances : relier les contrats aux implémentations¶
Une fois que le cœur dépend de contrats plutôt que d’implémentations concrètes, il faut bien relier les deux. C’est le rôle de la Dependency Injection (DI) : les implémentations requises sont fournies à la logique métier depuis l’extérieur, plutôt que créées à l’intérieur. Une composition root assemble ces composants, manuellement ou via un framework de DI. La Dependency Injection relie les pièces ; la Dependency Inversion définit comment elles doivent dépendre les unes des autres.
Garder les détails d’implémentation hors du cœur¶
Les détails d’implémentation doivent rester en dehors du cœur métier. Quand la logique du domaine contient des requêtes SQL, des annotations propres à un framework ou des appels à des SDK de fournisseurs, les choix techniques s’incrustent dans les règles métier. Le cœur devient plus difficile à modifier, tester et maintenir. Gardez ces détails derrière des frontières claires pour que la logique métier reste indépendante de la manière dont ils sont implémentés.
L’indépendance facilite les tests¶
Quand la logique métier dépend de contrats plutôt que d’implémentations concrètes, les tests peuvent remplacer les vraies dépendances par des mocks ou des stubs. Cela permet de tester les règles métier sans bases de données, services externes ni autre infrastructure. La même indépendance qui rend les détails techniques remplaçables rend aussi le cœur plus facile à tester.
Chapitre associé
Concevoir pour la testabilité explore les test seams, les mocks, les contrats et la stratégie de test plus large que permet la dependency inversion.































