Principes de l’architectureArchitectural principles

« Si les bâtisseurs bâtissaient des bâtiments comme les programmeurs écrivent des programmes, le premier pivert passant par là détruirait la civilisation. »"If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization."
- Gerald Weinberg- Gerald Weinberg

Quand vous architecturez et que vous concevez des solutions logicielles, vous devez penser à leur maintenabilité.You should architect and design software solutions with maintainability in mind. Les principes présentés dans cette section peuvent vous aider à prendre des décisions en matière d’architecture aboutissant à des applications propres et maintenables.The principles outlined in this section can help guide you toward architectural decisions that will result in clean, maintainable applications. D’une façon générale, ces principes vous guident dans la création d’applications à partir de composants individuels qui ne sont pas étroitement couplés à d’autres parties de votre application, mais qui communiquent plutôt via des interfaces explicites ou des systèmes de messages.Generally, these principles will guide you toward building applications out of discrete components that are not tightly coupled to other parts of your application, but rather communicate through explicit interfaces or messaging systems.

Principes de conception communsCommon design principles

Séparation des responsabilitésSeparation of concerns

Un principe à suivre lors du développement est la séparation des responsabilités.A guiding principle when developing is Separation of Concerns. Selon ce principe, le logiciel doit être divisé en fonction des types des tâches qu’il effectue.This principle asserts that software should be separated based on the kinds of work it performs. Par exemple, considérez une application qui inclut une logique pour identifier les éléments dignes d’intérêt à afficher à l’utilisateur, et qui met en forme ces éléments d’une façon particulière afin de les rendre plus visibles.For instance, consider an application that includes logic for identifying noteworthy items to display to the user, and which formats such items in a particular way to make them more noticeable. Le comportement responsable du choix des éléments à mettre en forme doit être distinct du comportement responsable de la mise en forme les éléments, car il s’agit de responsabilités distinctes qui n’ont qu’un lien de coïncidence entre elles.The behavior responsible for choosing which items to format should be kept separate from the behavior responsible for formatting the items, since these are separate concerns that are only coincidentally related to one another.

Du point de vue de l’architecture, les applications peuvent être créées de façon logique pour suivre ce principe, en séparant le comportement du cœur de métier de l’infrastructure et de la logique de l’interface utilisateur.Architecturally, applications can be logically built to follow this principle by separating core business behavior from infrastructure and user interface logic. Dans l’idéal, les règles et la logique métier doivent se trouver dans un projet distinct, qui ne doit pas dépendre d’autres projets dans l’application.Ideally, business rules and logic should reside in a separate project, which should not depend on other projects in the application. Cela permet de garantir que le modèle métier est facile à tester et peut évoluer sans être étroitement couplé aux détails d’implémentation de bas niveau.This helps ensure that the business model is easy to test and can evolve without being tightly coupled to low-level implementation details. La séparation des responsabilités est un aspect fondamental de l’utilisation des couches dans les architectures d’applications.Separation of concerns is a key consideration behind the use of layers in application architectures.

EncapsulationEncapsulation

Vous devez utiliser l’encapsulation pour isoler les unes des autres les différentes parties d’une application.Different parts of an application should use encapsulation to insulate them from other parts of the application. Les composants et les couches de l’application doivent pouvoir ajuster leur implémentation interne sans porter atteinte au fonctionnement de leurs collaborateurs dès lors que les contrats externes ne sont pas rompus.Application components and layers should be able to adjust their internal implementation without breaking their collaborators as long as external contracts are not violated. Une utilisation correcte de l’encapsulation permet d’atteindre un couplage faible et une modularité dans la conception des applications, car les objets et les packages peuvent être remplacés par d’autres implémentations tant que la même interface est conservée.Proper use of encapsulation helps achieve loose coupling and modularity in application designs, since objects and packages can be replaced with alternative implementations so long as the same interface is maintained.

Dans les classes, l’encapsulation est obtenue en limitant l’accès externe à l’état interne de la classe.In classes, encapsulation is achieved by limiting outside access to the class's internal state. Si un acteur externe veut manipuler l’état de l’objet, il doit le faire via une fonction bien définie (ou via une méthode setter de propriété), au lieu d’avoir un accès direct à l’état privé de l’objet.If an outside actor wants to manipulate the state of the object, it should do so through a well-defined function (or property setter), rather than having direct access to the private state of the object. De même, les composants d’application et les applications elles-mêmes doivent exposer des interfaces bien définies que leurs collaborateurs doivent utiliser, au lieu de permettre la modification directe de leur état.Likewise, application components and applications themselves should expose well-defined interfaces for their collaborators to use, rather than allowing their state to be modified directly. Ceci vous permet de faire évoluer au fil du temps la conception interne de l’application sans craindre d’altérer le bon fonctionnement des collaborateurs tant que les contrats publics sont respectés.This frees the application's internal design to evolve over time without worrying that doing so will break collaborators, so long as the public contracts are maintained.

Inversion des dépendancesDependency inversion

Le sens de la dépendance au sein de l’application doit être celui de l’abstraction, et non pas des détails d’implémentation.The direction of dependency within the application should be in the direction of abstraction, not implementation details. La plupart des applications sont écrites de telle sorte que les dépendances de compilation aillent dans le sens de l’exécution du runtime.Most applications are written such that compile-time dependency flows in the direction of runtime execution. Ceci produit un graphe des dépendances directes.This produces a direct dependency graph. Autrement dit, si le module A appelle une fonction dans le module B, qui appelle une fonction dans le module C, au moment de la compilation, A dépend de B, qui dépend de C, comme le montre la figure 4-1.That is, if module A calls a function in module B, which calls a function in module C, then at compile time A will depend on B which will depend on C, as shown in Figure 4-1.

Figure 4-1.Figure 4-1. Graphe des dépendances directes.Direct dependency graph.

L’application du principe d’inversion de dépendance permet à A d’appeler des méthodes sur une abstraction implémentée par B, ce qui permet à A d’appeler B à l’exécution, mais à B de dépendre d’une interface contrôlée par A au moment de la compilation (inversant ainsi la dépendance classique au moment de la compilation).Applying the dependency inversion principle allows A to call methods on an abstraction that B implements, making it possible for A to call B at runtime, but for B to depend on an interface controlled by A at compile time (thus, inverting the typical compile-time dependency). À l’exécution, le flux de l’exécution du programme reste inchangé, mais l’introduction d’interfaces signifie que des implémentations différentes de ces interfaces peuvent facilement être connectées.At run time, the flow of program execution remains unchanged, but the introduction of interfaces means that different implementations of these interfaces can easily be plugged in.

Figure 4-2.Figure 4-2. Graphe des dépendances inversées.Inverted dependency graph.

L’inversion des dépendances est une partie essentielle de la création d’applications faiblement couplées, car les détails d’implémentation peuvent être écrits de façon à dépendre d’abstractions de plus haut niveau et à les implémenter, et non pas l’inverse.Dependency inversion is a key part of building loosely-coupled applications, since implementation details can be written to depend on and implement higher level abstractions, rather than the other way around. Les applications qui en résultent sont plus faciles à tester, plus modulaires et plus maintenables.The resulting applications are more testable, modular, and maintainable as a result. La pratique de l’injection de dépendances est rendue possible par le respect du principe d’inversion des dépendances.The practice of dependency injection is made possible by following the dependency inversion principle.

Dépendances explicitesExplicit dependencies

Les méthodes et les classes doivent demander explicitement tous les objets de collaboration dont ils ont besoin pour fonctionner correctement.Methods and classes should explicitly require any collaborating objects they need in order to function correctly. Les constructeurs de classe offrent une occasion pour les classes d’identifier les éléments dont ils ont besoin pour être dans un état valide et pour fonctionner correctement.Class constructors provide an opportunity for classes to identify the things they need in order to be in a valid state and to function properly. Si vous définissez des classes qui peuvent être construites et appelées, mais qui fonctionnent correctement seulement si certains composants globaux ou d’infrastructure sont en place, ces classes trompent leurs clients.If you define classes that can be constructed and called, but which will only function properly if certain global or infrastructure components are in place, these classes are being dishonest with their clients. Le contrat du constructeur indique au client qu’il a seulement besoin des choses spécifiées (éventuellement de rien si la classe utilise seulement un constructeur sans paramètre), mais lors de l’exécution, il apparaît que l’objet avait en fait besoin d’autre chose.The constructor contract is telling the client that it only needs the things specified (possibly nothing if the class is just using a parameterless constructor), but then at runtime it turns out the object really did need something else.

En suivant le principe des dépendances explicites, vos classes et vos méthodes sont honnêtes avec leurs clients quant à ce dont elles ont besoin pour fonctionner.By following the explicit dependencies principle, your classes and methods are being honest with their clients about what they need in order to function. Ceci rend votre code mieux autodocumenté et vos contrats de codage plus conviviaux, car les utilisateurs leur font alors confiance dès lors qu’ils fournissent ce qui est nécessaire sous la forme de paramètres de méthode ou de constructeur, les objets avec lesquels ils travaillent se comportant alors correctement à l’exécution.This makes your code more self-documenting and your coding contracts more user-friendly, since users will come to trust that as long as they provide what's required in the form of method or constructor parameters, the objects they're working with will behave correctly at runtime.

Responsabilité uniqueSingle responsibility

Le principe de responsabilité unique s’applique à la conception orientée objet, mais il peut également être considéré comme un principe d’architecture similaire à la séparation des responsabilités.The single responsibility principle applies to object-oriented design, but can also be considered as an architectural principle similar to separation of concerns. Il stipule que les objets ne doivent avoir qu’une seule responsabilité et qu’une seule raison de changer.It states that objects should have only one responsibility and that they should have only one reason to change. Plus précisément, le seul cas où l’objet doit changer est quand la façon dont il effectue ce dont il est responsable doit être mise à jour.Specifically, the only situation in which the object should change is if the manner in which it performs its one responsibility must be updated. Suivre ce principe permet de produire des systèmes modulaires moins étroitement couplés, car de nombreux types de nouveaux comportements peuvent être implémentés sous forme de nouvelles classes, au lieu d’ajouter des responsabilités supplémentaires aux classes existantes.Following this principle helps to produce more loosely-coupled and modular systems, since many kinds of new behavior can be implemented as new classes, rather than by adding additional responsibility to existing classes. Ajouter de nouvelles classes est toujours plus sûr que modifier des classes existantes, car aucun code ne dépend déjà des nouvelles classes.Adding new classes is always safer than changing existing classes, since no code yet depends on the new classes.

Dans une application monolithique, nous pouvons appliquer le principe de responsabilité unique à un haut niveau aux couches de l’application.In a monolithic application, we can apply the single responsibility principle at a high level to the layers in the application. La responsabilité de la présentation doit rester dans le projet d’interface utilisateur, alors que la responsabilité de l’accès aux données doit être conservée au sein d’un projet d’infrastructure.Presentation responsibility should remain in the UI project, while data access responsibility should be kept within an infrastructure project. La logique métier doit être conservée dans le projet central de l’application, où elle peut être facilement testée et évoluer indépendamment des autres responsabilités.Business logic should be kept in the application core project, where it can be easily tested and can evolve independently from other responsibilities.

Quand ce principe est appliqué à l’architecture d’une application et mené à son aboutissement logique, vous obtenez des microservices.When this principle is applied to application architecture, and taken to its logical endpoint, you get microservices. Un microservice donné ne doit avoir qu’une seule responsabilité.A given microservice should have a single responsibility. Si vous devez étendre le comportement d’un système, il est généralement préférable de le faire en ajoutant des microservices supplémentaires, au lieu d’ajouter une responsabilité à un microservice existant.If you need to extend the behavior of a system, it's usually better to do it by adding additional microservices, rather than by adding responsibility to an existing one.

En savoir plus sur l’architecture des microservicesLearn more about microservices architecture

Ne vous répétez pas (DRY)Don't repeat yourself (DRY)

L’application doit éviter de spécifier à plusieurs endroits un comportement lié à un concept particulier, car il s’agit d’une source d’erreurs fréquente.The application should avoid specifying behavior related to a particular concept in multiple places as this is a frequent source of errors. À un moment donné, une modification des spécifications nécessite de changer ce comportement, et la probabilité que la mise à jour d’au moins une instance du comportement échoue entraîne un comportement incohérent du système.At some point, a change in requirements will require changing this behavior and the likelihood that at least one instance of the behavior will fail to be updated will result in inconsistent behavior of the system.

Au lieu de dupliquer la logique, encapsulez-la dans une construction de programmation.Rather than duplicating logic, encapsulate it in a programming construct. Faites de cette construction la seule autorité sur ce comportement, et faites en sorte que toutes les autres parties de l’application qui ont besoin de ce comportement utilisent la nouvelle construction.Make this construct the single authority over this behavior, and have any other part of the application that requires this behavior use the new construct.

Notes

Évitez de lier ensemble des comportements qui sont répétitifs seulement par coïncidence.Avoid binding together behavior that is only coincidentally repetitive. Par exemple, le simple fait que deux constantes différentes ont toutes les deux la même valeur ne signifie pas que vous ne devez avoir qu’une seule constante, dès lors qu’elles font conceptuellement référence à des choses différentes.For example, just because two different constants both have the same value, that doesn't mean you should have only one constant, if conceptually they're referring to different things.

Ignorance de la persistancePersistence ignorance

L’ignorance de la persistance fait référence aux types qui doivent être stockés, mais dont le code n’est pas affecté par le choix de la technologie de stockage.Persistence ignorance (PI) refers to types that need to be persisted, but whose code is unaffected by the choice of persistence technology. Ces types dans .NET sont parfois appelés des OCT (objets CLR traditionnels), car ils n’ont pas besoin d’hériter d’une classe de base particulière ni d’implémenter une interface particulière.Such types in .NET are sometimes referred to as Plain Old CLR Objects (POCOs), because they do not need to inherit from a particular base class or implement a particular interface. L’ignorance de la persistance est pratique, car elle permet au même modèle métier d’être stocké de plusieurs façons, ce qui offre davantage de flexibilité à l’application.Persistence ignorance is valuable because it allows the same business model to be persisted in multiple ways, offering additional flexibility to the application. Les choix de stockage peuvent changer au fil du temps, d’une technologie de base de données à une autre, ou bien d’autres formes de persistance peuvent être nécessaires en plus de ce avec quoi l’application a démarré (par exemple l’utilisation d’un cache Redis ou d’Azure DocumentDB en plus d’une base de données relationnelle).Persistence choices might change over time, from one database technology to another, or additional forms of persistence might be required in addition to whatever the application started with (for example, using a Redis cache or Azure DocumentDb in addition to a relational database).

Voici quelques exemples de violation de ce principe :Some examples of violations of this principle include:

  • Une classe de base obligatoire.A required base class.

  • Une implémentation d’interface obligatoire.A required interface implementation.

  • Des classes responsables de leur propre enregistrement (comme le modèle Enregistrement actif).Classes responsible for saving themselves (such as the Active Record pattern).

  • Un constructeur sans paramètre obligatoire.Required parameterless constructor.

  • Des propriétés nécessitant un mot clé virtuel.Properties requiring virtual keyword.

  • Des attributs obligatoires propres à la persistance.Persistence-specific required attributes.

La nécessité pour les classes de n’avoir aucune des caractéristiques ou aucun des comportements ci-dessus ajoute un couplage entre les types qui doivent être stockés et le choix de la technologie de stockage, ce qui rend difficile l’adoption de nouvelles stratégies d’accès aux données dans le futur.The requirement that classes have any of the above features or behaviors adds coupling between the types to be persisted and the choice of persistence technology, making it more difficult to adopt new data access strategies in the future.

Contextes délimitésBounded contexts

Les contextes délimités sont un modèle essentiel dans la conception pilotée par le domaine.Bounded contexts are a central pattern in Domain-Driven Design. Elles offrent un moyen de maîtriser la complexité dans les applications ou les organisations de grande ampleur en la fractionnant en modules conceptuels distincts.They provide a way of tackling complexity in large applications or organizations by breaking it up into separate conceptual modules. Chaque module conceptuel représente alors un contexte qui est distinct des autres contextes (et donc délimités) et qui peut évoluer indépendamment.Each conceptual module then represents a context which is separated from other contexts (hence, bounded), and can evolve independently. Chaque contexte délimité doit idéalement être libre de choisir ses propres noms pour les concepts qu’il contient, et doit avoir un accès exclusif à son propre magasin de persistance.Each bounded context should ideally be free to choose its own names for concepts within it, and should have exclusive access to its own persistence store.

Au minimum, les applications web individuelles doivent s’efforcer d’être leur propre contexte délimité, avec leur propre magasin de persistance pour leur modèle métier, au lieu de partager une base de données avec d’autres applications.At a minimum, individual web applications should strive to be their own bounded context, with their own persistence store for their business model, rather than sharing a database with other applications. La communication entre des contextes délimités se fait via des interfaces de programmation et non pas via une base de données partagée, ce qui permet à la logique métier et aux événements de se dérouler en réponse aux modifications qui se produisent.Communication between bounded contexts occurs through programmatic interfaces, rather than through a shared database, which allows for business logic and events to take place in response to changes that take place. Les contextes délimités correspondent étroitement aux microservices, qui dans l’idéal sont également implémentés sous la forme de leurs propre contexte délimité individuel.Bounded contexts map closely to microservices, which also are ideally implemented as their own individual bounded contexts.

Ressources supplémentairesAdditional resources