Dans Une brève introduction à PHP Namespacing, j’ai donné une introduction rapide à la fois aux espaces de noms et à la façon dont PHP les gère.
Mais il y a des problèmes architecturaux de plus haut niveau concernant les espaces de noms que beaucoup de gens m’ont signalés récemment, alors j’ai pensé que j’allais sortir un peu sur “papier”.
J’ai vu quelques façons principales d’organiser les espaces de noms. Je vais discuter des avantages et des inconvénients de chacun.
Remarques avant de commencer
- Il ne s’agit pas d’un article pédagogique, car il se peut qu’il n’y ait pas de méthode optimale pour chaque situation. Il s’agit simplement d’une discussion sur quelques options, et elle est principalement destinée à une organisation simple de l’espace de noms dans une application de petite à moyenne taille – pas l’entreprise, et pas nécessairement celles qui ont des problèmes architecturaux complexes.
- “Petit” et “Moyen” et “Grand” ici se rapportent uniquement au nombre de classes et d’entités, et n’ont rien à voir avec les lignes de code, le nombre d’utilisateurs du site ou quoi que ce soit d’autre.
- Nous prendrons l’exemple d’un
Command
qui envoie unReceipt
à unUser
. - Notre espace de noms global sera actuellement
App
juste pour la brièveté, mais vous pouvez remplacer cela parVendor\Package
.
A quoi servent les espaces de noms ?
Avant même de parler de la spécificité façons nous pouvons namespace, parlons-en pourquoi nous le faisons. Je suis redevable à Shawn McCool (comme toujours) pour m’avoir aidé à relier certaines de mes vagues pensées ici à des concepts informatiques réels.
Comme Shawn me l’a fait remarquer, le but de l’espacement des noms est cohésion: décrivant à quel point un code est connecté à un autre code. Il a souligné que dans d’autres langages, les espaces de noms sont appelés “Packages” ou “Modules” – et une fois que vous réalisez cela, vous comprenez que nous voyons les sous-espaces de noms comme de petits modules individuels qui devraient s’appuyer le moins possible sur d’autres modules. (encapsulation). Si la modularité est l’un des principaux objectifs finaux de notre espacement de noms, alors cela devient une (parmi plusieurs) métriques que nous pouvons utiliser pour juger d’un style particulier d’espacement de noms.
Bien sûr, même cette affirmation – que la modularité est un objectif principal de l’espacement des noms – fait l’objet d’un débat. Mais quand je l’entends, je l’aime.
Approches d’espacement de noms
OK, allons-y.
Espace de noms global
<?php namespace App;
class SendReceipt {}
src
Receipt
ReceiptRepository
SendReceipt
SendReceiptHandler
User
UserRepository
CreateUser
CreateUserHandler
Avantages
Je suppose qu’il est plus simple de ne pas avoir à gérer les sous-espaces de noms ? Sur un très petit app, cela pourrait être bien. Si vous avez cinq classes, qui dit que vous devez les sous-nommer du tout ? S’il s’agit d’un package unique avec un seul objectif, ou d’une application qui a un seul “module”, il se peut qu’il n’ait besoin de rien d’autre qu’un seul espace de noms global.
Les inconvénients
Au moment où vous obtenez une application d’une quelconque complexité, il va être difficile de trouver vos classes dans l’énorme bouillie de votre espace de noms global. Si vous avez une séparation d’identité ou d’objectif entre vos types de classes – par exemple, Utilisateurs contre Reçus – cet espacement de noms global les jette ensemble dans un grand pot. Pas du tout modulable.
Regrouper par motif
<?php namespace App\Commands;
class SendReceipt {}
src
Commands
SendReceipt
CreateUser
Entities
Receipt
User
Handlers
SendReceiptHandler
CreateUserHandler
Repositories
ReceiptRepository
UserRepository
Avantages
Lorsque vous voulez traquer une commande, vous savez exactement où elle se trouve. Si votre cerveau vous dit “Je dois modifier une de mes commandes. Laquelle ? Celle qui envoie les reçus”, c’est un bon choix. C’est un niveau plus organisé que d’ignorer les espaces de noms, mais pas si profond que vous serez ennuyé de l’utiliser dans un site de taille moyenne.
De plus, vos classes associées (par exemple, les commandes) peuvent vivre côte à côte ; vous pouvez voir tous les parallèles qu’il peut y avoir entre SendReceipt
et SendReminder
par exemple, et voyez comment ils se connectent tous les uns aux autres.
Cette méthode vous permet également d’architecturer les relations entre les types de classe par programmation. Par exemple, un bus de commande peut savoir que le gestionnaire d’une commande (qui vit à App\Commands\{commandName}
) vit toujours à App\Handlers\{commandName}Handler
.
Les inconvénients
Le faire de cette façon vous laisse avec des classes dans le même contexte répartis sur de nombreux espaces de noms différents. Par exemple, vous pourriez avoir App\Commands\SendReceipt
, App\Receipt
ou App\Entities\Receipt
, App\Providers\ReceiptServiceProvider
, App\Handlers\Commands\SendReceiptHandler
, App\Repositories\ReceiptRepository
, et ainsi de suite. Toute votre logique de reçu, éparpillée partout.
Si l’on mise sur l’encapsulation et la modularité, ce regroupement n’est pas gagnant. Parce que nous avons réparti tout le code sur la facturation, par exemple, sur l’ensemble de notre paysage d’espace de noms, l’organisation de la classe ne se concentre pas sur la création d’un module de facturation. Les classes sont côte à côte simplement parce qu’elles suivent le même modèle architectural, pas parce qu’elles sont réellement liées.
Regrouper par contexte
<?php namespace App\Billing;
class SendReceipt {}
src
Billing
Receipt
ReceiptRepository
SendReceipt
SendReceiptHandler
User
User
UserRepository
CreateUser
CreateUserHandler
Avantages
Si vous travaillez uniquement dans la facturation en ce moment, vous savez que vous aurez tout liés à la facturation réunis en un seul endroit. Pour vos reçus, l’entité, la commande, le gestionnaire de commandes, le référentiel, etc., tous réunis dans un ensemble agréable et soigné, facile à traiter en tant que groupe unique.
C’est là que nous commençons à faire l’expérience de l’encapsulation et de la modularité. Toutes nos classes liées à la facturation, quel que soit leur modèle de conception, sont réunies au même endroit, ce qui nous aide à les regrouper mentalement, en commençant même à les considérer comme une unité pouvant vivre en dehors de cette application.
Les inconvénients
Vos commandes sont maintenant réparties sur la base de code. Vos dépôts aussi. Et vos entités. Et vos gestionnaires de commandes.
Regrouper par contexte et modèle
<?php namespace App\Billing\Commands;
class SendReceipt {}
src
Billing
Entities
Receipt
Repositories
ReceiptRepository
Commands
SendReceipt
Handlers
SendReceiptHandler
User
Entities
User
Repositories
UserRepository
Commands
CreateUser
Handlers
CreateUserHandler
Avantages
Le séparer de cette manière vous donne le plus haut niveau de séparation de l’espace de noms – cela seul est un pro pour certaines personnes. Ceci est particulièrement utile si vous avez une grande base de code avec beaucoup de classes – plus il y a de classes, plus vous apprécierez les options supplémentaires de séparation. Imaginez ajouter un UpdateUser
commande, un DeleteUser
commande, un Subscription
entité et référentiel et gestionnaires associés…
Tout comme Group by pattern, vous pouvez associer des classes par programme.
Et bien que vos classes soient séparées par modèle, elles sont toujours regroupées par contexte, vous avez donc toujours tous vos Receipt
coder ensemble en un seul endroit. Nous obtenons toujours l’avantage de la modularité que nous avons fait dans le groupe par contexte.
Les inconvénients
Plus la définition de l’espace de noms est longue pour votre classe, plus l’énergie mentale doit être dépensée pour comprendre l’ensemble de la pile d’espaces de noms. Il y a plus de possibilités de fautes de frappe et de confusion. Et avec une application de petite ou moyenne taille, cela peut sembler exagéré.
Étant donné que vous regroupez vos classes par modèle au niveau le plus bas, vous ne bénéficiez pas autant de l’avantage du regroupement par contexte que le style Regrouper par contexte.
Goûter
OK, donc c’est beaucoup de théorie abstraite à ce sujet. Mais qu’en est-il d’un exemple concret ? J’ai pris quelques classes de SaveMyProposals comme exemple. Voyons comment nous gérons les Talks, les Conférences, et comment nous proposons les Talks aux conférences :
Espace de noms global
app
Conference
ConferenceRepository
CreateConference
CreateConferenceHandler
CreateTalk
CreateTalkHandler
DeleteConference
DeleteConferenceHandler
DeleteTalk
DeleteTalkHandler
ProposeTalkToConference
ProposeTalkToConferenceHandler
RetractTalkProposal
RetractTalkProposalHandler
Talk
TalkRepository
UpdateConference
UpdateConferenceHandler
UpdateTalk
UpdateTalkHandler
Regrouper par motif
app
Commands
CreateConference
CreateTalk
DeleteConference
DeleteProposal
DeleteTalk
ProposeTalkToConference
RetractTalkProposal
UpdateConference
UpdateTalk
Entities
Conference
Proposal
Talk
Handlers
CreateConferenceHandler
CreateTalkHandler
CreateProposalHandler
DeleteConferenceHandler
DeleteProposalHandler
DeleteTalkHandler
ProposeTalkToConferenceHandler
RetractTalkProposalHandler
UpdateConferenceHandler
UpdateTalkHandler
Repositories
ConferenceRepository
TalkRepository
Regrouper par contexte
app
Conferences
Conference
ConferenceRepository
CreateConference
CreateConferenceHandler
DeleteConference
DeleteConferenceHandler
UpdateConference
UpdateConferenceHandler
Talks
CreateTalk
CreateTalkHandler
DeleteTalk
DeleteTalkHandler
ProposeTalkToConference
ProposeTalkToConferenceHandler
Talk
TalkRepository
RetractTalkProposal
RetractTalkProposalHandler
UpdateTalk
UpdateTalkHandler
Regrouper par contexte et modèle
app
Conferences
Commands
CreateConference
DeleteConference
UpdateConference
Entities
Conference
Handlers
CreateConferenceHandler
DeleteConferenceHandler
UpdateConferenceHandler
Repositories
ConferenceRepository
Talks
Commands
CreateTalk
DeleteTalk
ProposeTalkToConference
RetractTalkProposal
UpdateTalk
Entities
Talk
Handlers
CreateTalkHandler
DeleteTalkHandler
ProposeTalkToConferenceHandler
RetractTalkProposalHandler
UpdateTalkHandler
Repositories
TalkRepository
Conclusion
Alors, quelle est la réponse ?
Ça dépend.
C’est possible que les structures organisationnelles plus simples fonctionnent mieux pour les applications avec moins de classes et d’entités, tandis que les structures organisationnelles plus grandes correspondent mieux aux systèmes organisationnels plus robustes. Mais ce n’est pas une règle stricte. Je ne suis même pas sûr à 100% que ce soit une règle du tout.
Je pense que les idées de modularité et d’encapsulation donnent du temps à votre cerveau. Réfléchissez à la manière dont vous le concevriez si chaque sous-espace de noms devait être supprimé des autres.
Mais à la fin, je dirais qu’il suffit de les essayer tous. Déterminez ce que vous aimez. Déterminez ce qui vous dérange. Déterminez les avantages que vous tirez de chacun. Vous comprendrez cela.