DÉVELOPPEMENT
Petite vulgarisation de code moderne pour tech et non-tech
Publié le : 11 Mars 2022
13 min.
Partager
TDD, BDD, DDD, CQRS, Event Sourcing, Hexagonal … autant de termes barbares qu’on entend de temps à autre dans la bouche des techs. Dans cet article, je vous propose de présenter certains d’entre eux, et de vous donner des aperçus pour décider s’il s’agit de bonnes orientations pour votre projet.
La version pour non-tech
Ces techniques sont relativement indépendantes. Elles permettent de piloter le découpage et l’organisation technique du projet. En voici un aperçu :
Approche | Principe | Intérêt |
BDD | Commencer par décrire le comportement métier par du code | Se concentrer sur le besoin métier |
TDD | Commencer par écrire les tests techniques | Assurer la testabilité et le découpage |
CQRS | Séparer les flux de lecture et d’écriture | Éviter les effets indésirables |
Ubiquitous language | Le vocabulaire est clair et identique pour tous les acteurs du projet, y compris dans le code | Éviter les malentendus |
Couche Domaine | Mettre à part le code du métier, sans dépendance de librairies | Maintenance simplifiée |
Value object | Manipuler les valeurs du métier sous la forme d’objets métier, au lieu de chaînes de caractères, d’entiers, etc. | Porter le sens et la validation des données |
Hexagonal | Identifier et exposer les interactions du métier avec les utilisateurs d’une part (cas d’usage), et l’infrastructure d’autre part (interfaces) | Réutilisation de code et maintenabilité |
Event Sourcing | Considérer l’état d’un objet comme la succession de ses changements | Cohérence des données |
Les points essentiels :
- Utiliser ces techniques prend plus de temps et requiert plus d’expertise que des méthodes plus traditionnelles.
- Leur pertinence est à mettre en balance avec des critères comme la criticité du besoin, la maintenabilité requise, l’équipe projet, l’implication du demandeur
La version pour tech
Avant toute chose, sachez que, de manière générale, ces techniques n’ont pas pour objectif d’améliorer la rapidité de développement. Les techniques de développement rapide existent depuis longtemps. Le coût d’une application en est ainsi réduit efficacement, mais cette tendance apporte son lot d’inconvénients. Pour prendre une analogie, c’est comme si on inventait des méthodes pour bâtir un édifice très rapidement. Mais en contrepartie, le bâtiment serait plus compliqué à entretenir, il aurait moins de possibilité d’évolutions, etc.
C’est donc en pensant au long-terme que ces outils et techniques viennent apporter leurs contributions.
Du besoin au code
Créer des applications, c’est comprendre des besoins et proposer des solutions. Nous appelons cela «le métier», même si cela ne se rapporte pas nécessairement à un domaine professionnel.
Pour décrire ce métier, nous avons l’habitude de l’écrire sous la forme d’un document rédigé. Ce sont les spécifications. En voici un exemple :
Juste avec cette petite phrase, on voit émerger les concepts du métier : -Les objets métier : le client, le produit, le panier -Une action métier : ajouter un produit au panier -Un retour d’information : connaître le montant total du panier
Pour PHP, cet objet pourrait être comme ceci :
Le principal avantage ici, c’est que l’on garde le lien direct entre la spécification et le code. On sait à quelle règle il correspond, et pourquoi on l’a fait. Ça n’a l’air de rien, mais parlez-en avec un développeur qui a dû maintenir un projet, on se demande régulièrement : mais pourquoi on fait ça ? On peut mettre en place tout le comportement de l’application juste avec ces règles. Au passage, écrire le code oblige un tel formalisme qu’il permet aussi de mettre en lumière les incohérences, les manquements … Autre grand intérêt : avec cette façon de procéder, on divise la complexité du métier en sous-ensembles relativement indépendants.
Spécifications comme du code
Allons un peu plus loin : si on écrivait ces specs avec du code ? Il existe même un outil en php pour nous aider dans cette démarche : phpSpec Ouvre une nouvelle fenêtre http://phpspec.net
Petit clin d’œil au commit strip qui illustre l’idée parfaitement : Ouvre une nouvelle fenêtre https://www.commitstrip.com/fr/2016/08/25/a-very-comprehensive-and-precise-spec/
Pour notre exemple de boutique :
On peut lire et comprendre cette règle sans avoir besoin d’être développeur. Et on peut voir spontanément les spécifications qui découlent de cette règle. Par exemple, que le montant devrait être de zéro par défaut.
De règle en règle, on peut décrire toute l’application ici, sans la créer. C’est le rôle des spécifications.
Avantage énorme pour écrire les spécifications dans ce format : on peut vérifier que notre application les respecte bien ! Nous venons en fait d’écrire les tests métiers, et ce sont les plus importants !
Dans cette partie, vous avez découvert ce qu’est le « design by Specification » − Conception basée sur les spécifications
Et le « métier » du développeur ?
Quand on a spécifié toute l’application, vous pensez que notre travail est finalisé ? Pas du tout !
Pour le moment, les actions et les objets ne peuvent être gérées que par du code. Selon les applications, on a besoin de relier la base de code du métier à une ou plusieurs sources d’interaction. Elle peut être de plusieurs types :
- une application graphique sur votre OS, dans le système de fenêtres
- une API
- un site internet
- une application sur téléphone
- voire même un objet connecté
Ça, c’est le « métier » du développeur. Et sa tâche maintenant consiste à faire des ponts entre son métier et celui du client.
Concentrons-nous sur une source d’interaction web. Selon le principe de ne pas réinventer la roue, nous allons utiliser un framework (cadriciel) qui permet déjà de faire les opérations de base telles que comprendre une requête HTTP (par exemple un visiteur qui navigue sur notre site) et y donner une réponse.
Il nous faut donc brancher les actions sur le serveur aux actions métier. Avec un contrôleur Symfony, cela donnerait :
Comme on peut le voir, la partie « HTTP » de l’infrastructure est capable de gérer les requêtes HTTP et d’y répondre, en faisant appel au métier. Dans notre jargon, on parle de couches (layers). On peut déjà distinguer deux couches :
- L’infrastructure est chargée des interactions avec le monde extérieur.
- Le métier, qui est responsable pour faire appliquer les règles métier.
La première couche peut faire appel à la deuxième, mais on s’interdit de faire le chemin inverse. D’abord, parce qu’on ne souhaite pas que le métier soit contraint par l’infrastructure. Ensuite, d’une façon plus pragmatique, on s’assure ainsi d’avoir une base de code qui ne dépend pas d’éléments extérieurs tel qu’un framework, ou une librairie. Très concrètement : si une modification est apportée sur la façon d’utiliser Symfony (et il y en a !), on ne veut pas avoir à modifier la couche métier pour y correspondre.
Cette séparation, c’est le cœur de ce que l’on nomme DDD, Domain Driven Design − Conception pilotée par le métier.
Stocker des données, et autres relations extérieures
À ce point, nous avons donc une base de code métier indépendante au milieu, une infrastructure autour, et des composants externes.
Un composant externe, ça peut être tout type de brique indépendante :
- une base de données (PostgreSQL, …)
- un moteur de recherche (elastic search, …)
- un système de fichiers (s3, …)
- un système de file d’attente de messages (AMQP, …)
- un web-service ( Ouvre une nouvelle fenêtrehttps://api.insee.fr, …)
- une API externe ( Ouvre une nouvelle fenêtrehttps://ifttt.com/, …)
D’un point de vue métier, on souhaite clairement ignorer ces détails bassement techniques ! Mais on souhaite indiquer des actions demandées par le métier. Ce qui compte, ce n’est pas comment l’action est réalisée (techniquement), mais quel est son intention.
Si par exemple on souhaite sauvegarder le panier, on utiliserait la magie des contrats que sont les Interfaces.
Objet métier :
Interface métier :
Implémentation infrastructure :
Pour résumer : un cœur de métier au centre, un ensemble de briques autour pour interagir avec son environnement, et une couche d’adaptation entre les deux. Voilà les bases de ce que l’on peut nommer « l’architecture Hexagonale ». Dans un contexte Hexagonal :
- Les interactions avec l’utilisateur sont représentées comme le côté gauche de l’application.
- Les interactions avec l’environnement sont représentées comme le côté droit de l’application.
Exposer des points d’entrée
Revenons à la couche métier, au cœur de notre application. Nous allons déclarer explicitement les actions que nous proposons, dans une nouvelle couche entre l’infrastructure et le métier. On appelle généralement cette couche « Application » et ces actions des « cas d’usage ».
Voici par exemple le cas d’usage « ajout au panier ».
Définissons également l’objet que l’on attend en entrée
Ainsi, côté infrastructure (Symfony), faisons appel à ce cas d’usage :
Par cette technique, on montre clairement ce que l’on peut faire dans le métier. Vous pouvez imaginer un travail en deux équipes (ou deux développeurs) :
- L’une est chargée de mettre en place la couche métier, et les cas d’usage.
- L’autre est chargée de faire coller l’infrastructure à cette couche métier.
Valider
Dans le milieu informatique, on entend qu’il vaut mieux avoir de bonnes données dans un mauvais programme, qu’un excellent programme avec des données de mauvaise qualité. Profitons donc de cette démarche pour nous assurer de la validité et de la cohérence des données. La validation concerne toutes les couches de notre application :
- Dans la couche infrastructure, on pourra préciser qu’on ne peut ajouter au panier que des produits qui existent (par une référence en base de données).
- Dans la couche application, on pourra vérifier que l’identifiant est bien un nombre entier.
- Dans la couche métier, on pourra vérifier que tous les produits dans le panier sont de la même catégorie (parce que pourquoi pas ?)
Dans tous les cas, on pourra déclencher des erreurs de validation pour en afficher un retour à l’utilisateur. Elles seront interceptées au niveau de l’infrastructure, car leurs mises en forme dépendra du canal d’interaction entre l’utilisateur et le logiciel.
On pourra l’ajouter au contrôleur Symfony
Lire ou écrire ?
Parmi les actions que l’on peut réaliser dans l’application, on peut identifier deux grandes catégories : Lire ou écrire. C’est un risque qui existe sur une application, obtenir des effets de bord qui provoque des modifications lorsqu’on ne fait que de la lecture.
Par exemple, si un produit n’existe plus quand on demande la liste des produits dans le panier, le système peut retirer le produit du panier. Or, cette requête ne devrait pas modifier l’état des données sur le serveur.
Par ailleurs, la façon d’apporter des modifications aux données peut se faire différemment de la façon de manipuler ces données pour affichage. Par exemple, pour afficher les produits au panier, on peut avoir besoin de leurs montants, leurs catégories, leurs tags … Alors que ces données sont superflues quand on veut juste ajouter le produit au panier. C’est donc tout le code qui sera différent pour les opérations en lecture et en écriture.
Pour clarifier cette séparation, on peut s'appuyer sur le modèle CQRS (Command Query Responsibility Segregation)
Attention cependant à bien faire la balance entre le coût et le bénéfice, qui se situe essentiellement dans l’optimisation des performances.
Se comprendre !
Une autre tendance qui est amenée par le DDD, est la nécessité d’un vocabulaire unique. L’idée, c’est que les concepts métiers soient partagés par tous les acteurs du projet par les mêmes termes. Nous l’avons utilisé dans les exemples plus haut.
Au final, cette pratique dissipe beaucoup de malentendus, et elle rend le code beaucoup plus accessible, y compris à des membres de l’équipe qui ne sont pas des profils techniques.
Cette technique porte le nom so-british de « Ubiquitous Language »
Dans nos contrées, quand la communication avec le métier se fait en français, il faut statuer si on traduit ou non les termes métier. C’est une décision qui doit être prise au niveau du projet, car les deux solutions ont leurs avantages et leurs inconvénients. getPanier ou getCart ? Utiliser les mêmes termes ou garder la même langue dans le code ?
Pour finir
Nous aurions pu compléter ce tour d’horizon pragmatique par le développement piloté par les tests, ou l’intérêt de mettre en place une API ou de la standardisation de la communication backend / frontend … Les champs ouverts par ces approches et leurs confluences ouvrent la voie à du code toujours plus lisible. Attention cependant à ne pas faire le raccourci que ces approches réduisent la complexité. Schématiquement, celle-ci est étalée sur une plus grande surface (nombre de fichiers) plutôt que empilée en tas (taille des fichiers).
Bref, j’espère vous avoir apporté un aperçu de ce que peuvent être ces outils et techniques qui permettent d’améliorer la qualité d’un programme informatique. Comme vous avez pu le découvrir, il s’agit comme toujours de bien faire la part des choses entre le bénéfice et le risque. Avant de prendre une décision d’utiliser telle ou telle technique, identifiez votre besoin, la criticité, la durée de vie de votre projet. Mais aussi d’autres critères comme la variété des développeurs qui devront intervenir sur le projet.
Pour finir, il me semble que le code ainsi produit devient plus compréhensible par des non-développeurs, et permet d’éviter bien des écueils et malentendus ! Partagez nous votre point de vue ou votre expérience sur ce sujet. Ça m’intéresse beaucoup, en particulier si vous n’êtes pas technicien du code et que vous avez réussi à arriver au bout de cet article !
Intéressé·e par le code moderne ?
Découvrez notre expertise DDD !
Expertise DDDVincent Beauvivre
Développeur back
Partager