Getting Your Foundation Right @php[architect]

Volume 25 - Numéro 2 (02/2026) by Oscar Merida

Les logiciels sont finalement écrits pour les humains, pas seulement pour les machines. Un processeur se contenterait parfaitement d'un code assembleur ou machine, mais ceux d'entre nous qui ont des processeurs humains ont besoin d'abstractions, d'une syntaxe lisible et d'un code source organisé pour naviguer et comprendre des systèmes complexes. C'est là qu'intervient l'architecture logicielle, qui fournit le cadre structurel permettant de garantir que nos applications sont fiables, efficaces et maintenables dans le temps. Ce mois-ci, nous explorons comment l'architecture allie art et science pour faire face aux compromis inévitables que chaque projet doit faire. Je vais vous montrer le rôle des Architectural Decision Records (records de décisions architecturales) pour documenter le « pourquoi » des choix clés, puis je vais vous démontrer comment nous pouvons appliquer automatiquement ces normes à l'aide de règles PHPStan personnalisées et du package PHPArkitect.

Quand on parle d'« architecture logicielle », qu'entend-on par là ? Les processeurs parlent le langage machine et n'ont pas besoin de toutes les abstractions et du sucre syntaxique dont les humains ont besoin pour écrire un programme. L'architecture, lorsque nous faisons référence aux bâtiments et aux structures, allie l'art et la science pour concevoir des bâtiments, des croquis aux plans, jusqu'à l'édifice final. Un stade aura des exigences différentes de celles d'un immeuble d'habitation. Un architecte se débat avec la manière de concevoir le bâtiment pour qu'il soit stable, esthétique et fonctionnel. L'architecture logicielle n'est pas très différente. Elle fournit des structures aux logiciels afin que les gens puissent les comprendre. Sur le web, bien que nous utilisions principalement une architecture client-serveur, nous pouvons avoir un monolithique ou un microservice en arrière-plan. Nous pouvons utiliser des bases de données relationnelles ou documentaires, en fonction des besoins de stockage. Pour gérer l'évolutivité, nous pouvons produire du HTML statique à partir d'un CMS pour les sites riches en contenu ou disposer d'une application à évolutivité horizontale derrière un load balance pour les interfaces utilisateur hautement interactives. Dans ces deux domaines, l'architecture est concernée par des décisions fondamentales qui sont difficiles et coûteuses à modifier par la suite. Vous voyez le schéma qui se dessine ? Nous faisons des compromis en fonction de nos besoins. Par exemple, si nous servons des fichiers HTML statiques, les mises à jour de contenu ne seront pas disponibles immédiatement après leur validation, car le processus de publication des fichiers prend du temps. Les sites statiques gèrent les pics de trafic sans sourciller, mais ne disposent pas des informations les plus récentes. Vous ne recommanderiez pas un site statique si « fournir des données en temps réel » est une exigence commerciale. Au niveau architectural, nous ne nous préoccupons pas autant des détails de mise en œuvre. Ici, nous nous intéressons au comment et au pourquoi, en nous concentrant sur :

  • Fiabilité: s'assurer que le système gère les pannes et les erreurs avec élégance.
  • Efficacité: le logiciel répond aux exigences de l'entreprise et aux besoins des utilisateurs.
  • Maintenabilité: nous pouvons facilement mettre à jour, améliorer et faire évoluer le système.
Ce ne sont pas les seuls facteurs à prendre en compte. Nous devons notamment réfléchir très tôt aux besoins en matière de sécurité. Les décisions que nous prenons à ce stade nous permettent de discuter des choix de conception avant de commencer à construire quoi que ce soit, de définir la manière dont les chefs de projet vont planifier et répartir le travail, de réduire les risques et les chances d'échec, et de communiquer avec les parties prenantes afin de comprendre leurs besoins. Il n'est pas nécessaire d'avoir un projet d'envergure pour se soucier de la bonne mise en place de ces éléments. Même un petit projet comme celui de notre client bénéficie d'un bon « plan directeur » à mesure que des fonctionnalités sont ajoutées et des bogues corrigés.

Pour approfondir ce sujet, lisez A Primer for Emerging Software Architects (Introduction aux architectes logiciels émergents ) d'Anders Tornblad.

Registres des décisions architecturales - Architectural Decision Records

Envisagez de consigner les choix importants dans un registre des décisions architecturales. Il s'agit de documents simples, le format Markdown est utile, car il permet à votre équipe de documenter la manière dont elle est parvenue à une décision architecturale unique, notamment :

  • Titre: un nom court et descriptif
  • Contexte: la situation actuelle et les exigences qui nécessitent une décision
  • Decision: la solution que vous avez choisie
  • Consequences: impact prévu
  • État: soit proposé, accepté, remplacé ou déprécié
Vous pouvez également inclure les avantages/inconvénients discutés, les alternatives envisagées et les liens vers les ressources pertinentes. Conservez-les dans votre référentiel de projet avec votre code pour aider les nouveaux membres de l'équipe à s'intégrer et à comprendre l'historique du projet. En capturant l'état de chaque décision, vous obtenez une « trace écrite » de la dette technique qui montre exactement où vous en étiez et pourquoi vous avez évolué. Une bonne règle de base consiste à créer un ADR si vous passez plus de 15 à 30 minutes à débattre de la manière de faire quelque chose. Pour lire les ADR pour les projets basés sur PHP, Lullabot a publié ses choix de conception pour les projets Drupal (ADRs Drupal sur Lullabot ). Ils expliquent pourquoi ils utilisent DDEV pour les environnements locaux4 et les types stricts dans le code PHP5. Ce dernier explique non seulement pourquoi les types stricts sont utiles, mais montre également comment trouver des fichiers sans types stricts à l'aide de différents outils. Les ADR ne doivent pas nécessairement être longues. Exemple la ADR "Self-host Third-Party Static Assets" utilise des puces pour rester concis :
Self-host Third-Party Static Assets
Le référentiel Markdown Architectural Decision Records (Markdown Architectural Decision Records ) fournit des modèles que vous pouvez utiliser comme point de départ lorsque vous les ajoutez à un projet. J'ai utilisé le modèle de base en y apportant quelques modifications et j'ai pris des décisions documentaires (Markdown ADR records ) concernant les normes de codage et les conteneurs de services. Chacun d'entre eux expose brièvement le problème qui a motivé une décision, décrit les alternatives envisagées, consigne la décision finale et énumère les conséquences éventuelles. Cela permet de disposer d'un historique informatif expliquant pourquoi le projet est construit tel qu'il est. Enfin, ils empêchent la dérive du projet en rappelant aux équipes pourquoi une décision a été prise et en évitant de s'engager accidentellement dans une autre direction.

Application de l'architecture de classe

En plus de documenter les décisions, nous pouvons appliquer certaines décisions et conventions architecturales à notre code PHP à l'aide d'outils automatisés. Nous pouvons utiliser des règles personnalisées dans PHPStan pour vérifier les méthodes dans nos classes ou pour empêcher l'utilisation de fonctions indésirables. Cependant, l'écriture de règles PHPStan est complexe. Si vous avez besoin d'appliquer des règles au niveau des classes, consultez le package relativement nouveau phparkitect/phparkitect.

Aucune affiliation avec php[architect].

Règles Phpstan personnalisées

Nous pourrions créer une règle pour nous assurer de ne pas appeler les fonctions die() et eval() nulle part dans notre code. Nous pouvons souhaiter gérer la fin du programme de manière plus élégante en lançant une exception, en enregistrant une erreur ou en redirigeant l'utilisateur vers une page d'erreur. Pour le client de l'auteur de l'article : spacetraders , je souhaite vérifier les méthodes des classes de contrôleurs et m'assurer qu'elles ne prennent aucun paramètre et renvoient un objet de type Psr\Http\Message\ResponseInterface. Si nous pouvons intégrer ces tests à nos revues de code, le réviseur n'aura pas à se souvenir de chaque règle et nuance, ce qui réduira la charge mentale requise. Ce n'est pas quelque chose que nous pouvons faire avec des interfaces ou des typehints. Le listing 1 implémente une règle personnalisée pour appliquer ces règles. Comme il s'agit de code, nous devons être précis, pour tout contrôleur dans le namespace Controller:

  • S'il ne se trouve pas dans le namespace Controller, ignorez-le.
  • S'il ne s'agit pas d'une méthode publique ou s'il s'agit de la méthode __construct(), ignorez-la.
  • Si la méthode publique est héritée ou provient d'un trait, ignorez-la. Nous nous intéressons uniquement à la vérification du comportement des méthodes avec un attribut Route.
  • Si la méthode publique comporte des arguments, signalez l'erreur.
  • Si la méthode utilise la stratégie d'application, elle doit renvoyer un objet ResponseInterface.
  • Si la méthode n'utilise pas la stratégie d'application, elle doit renvoyer un tableau. Cela suppose que nous voulons rendre la réponse sous forme de JSON.

Listing 1

Listing 1 (cont.)

Après plusieurs ajustements pour que la règle fonctionne correctement, elle a vérifié tout mon code et a trouvé une erreur. J'avais une méthode renvoyant une réponse HTML qui n'utilisait pas la stratégie d'application. L'application de cette règle ne se limite pas à garder notre code propre et à respecter les normes. Il aide les développeurs en indiquant que chaque route utilisant la stratégie d'application doit renvoyer une réponse. Cela évite d'écrire du code défensif comme if (is_array($response) lors de la gestion des routes.

Utilisation de Phparkitect

Suivez les instructions d'installation1 pour installer avec Composer. Installer Phparkitect L'outil recherche les règles dans phparkitect.php à la racine de votre projet lorsqu'il est exécuté via vendor/bin/phparkitect. Le listing 2 contient les règles que j'ai ajoutées pour l'application Spacetraders. Ces règles déterminent la manière dont les classes sont organisées et reliées entre elles. Nous vérifions les éléments suivants :

  • Les noms des classes dans namespace Controller doivent se terminer par Controller.
  • Toute classe dans le namespace Client doit être une instance de la classe Client. L'utilisation de IsA() correspond à la fois à la classe de base et à n'importe laquelle de ses classes enfants.
  • Tout fichier dans le namespace Interface doit être uniquement une Interface.
  • Toute classe dans le namespace Middleware doit implémenter l'interface Middleware.
  • Tout fichier dans le namespace Trait doit être un Trait.
  • Toute classe dans le namespace Value ne peut être composée que d'autres classes Value.

Listing 2 Listing 2

Ces règles s'exécutent plus rapidement que les règles PHPStan et nous aideront à garder le code propre et bien organisé. Par exemple, les nouveaux développeurs peuvent voir où trouver tous nos contrôleurs et savoir où ils doivent en créer de nouveaux. En cas d'erreur, par exemple une nouvelle classe de contrôleur qui ne respecte pas les conventions attendues, phparkitect les répertorie.

Exécution automatique de Phparkitect

Je souhaite appliquer les règles PHPArkitect avant que les nouveaux commits ne soient poussés vers le dépôt et dans le cadre des vérifications CI sur GitHub. J'ai d'abord ajouté la commande phparkitect à la section pre-push des hooks dans composer.json. J'ai ensuite exécuté cghooks update pour actualiser mes hooks git. Hook pre-push Comme cela s'exécute en arrière-plan, je n'ai pas besoin du résultat complet à chaque exécution. Le drapeau format envoie un rapport succinct si des violations apparaissent. Rapport violations Bien que phparkitect dispose d'une action GitHub officielle, il lance un conteneur Docker pour exécuter l'application. Vous pouvez également l'exécuter dans une action existante. J'ai mis à jour .github/workflows/analyze avec la nouvelle étape suivante afin qu'il s'exécute après PHPStan et PHPCodeSniffer pour toute PR : .github/workflows/analyze

Conclusion

L'architecture n'est pas un plan statique. Dans une base de code vivante, il s'agit d'un processus continu de prise de décision et d'application. Documenter les décisions et automatiser les vérifications peut parfois sembler fastidieux, mais c'est essentiel pour gérer la dette technique à mesure que le projet vieillit. En documentant les décisions à l'aide d'ADR et en automatisant les « comment » à l'aide d'outils tels que PHPStan et PHPArkitect, nous pouvons transformer l'architecture logicielle d'objectifs abstraits en un système tangible et autocorrecteur. Considérez-les comme des glissières de sécurité qui nous aident à avancer plus rapidement en toute confiance. N'oubliez pas que lentement mais sûrement, on va vite.