J'ai vu au fil des années de nombreux développeurs utiliser le modèle Repository avec Laravel, essayant d'appliquer des concepts d'architecture propre à leurs applications, mais comprenant souvent mal le concept général de l'utilisation d'un framework comme Laravel. .
Avant de commencer et d'éviter quelques pierres que vous pourriez me lancer, laissez-moi être clair : voici mon opinion en tant qu'ingénieur logiciel qui a travaillé avec plusieurs langages, frameworks, créé des logiciels à partir de zéro et maintenir les anciennes bases de code héritées. Ma parole n'est pas définitive et, comme toujours, je pense que la réponse la plus acceptable à toute question de génie logiciel est : "Cela dépend de ce que vous êtes prêt à sacrifier pour résoudre votre problème."
Alors, sans plus attendre, entrons dans le vif du sujet.
Clean Architecture est une philosophie de conception de logiciels qui vise à créer des systèmes faciles à entretenir, à tester et à comprendre. Il met l'accent sur la séparation des préoccupations et la création de frontières entre les différentes parties du système pour garantir que les changements intervenus dans une partie n'affectent pas les autres. Cette architecture a été popularisée par Robert C. Martin (Oncle Bob) et est souvent utilisée pour guider l'organisation du code de manière résiliente aux changements de règles métier ou d'exigences techniques.
En bref, ce que Bob propose, c'est que votre application soit divisée en couches qui suivent des principes clés pour permettre à votre logique métier d'être découplée de toute dépendance externe, leur donnant ainsi une mobilité et un contexte de responsabilité unique. Mais il ne s’agira pas d’un article sur l’architecture épurée ; Je voulais juste nous mettre sur la même longueur d'onde à ce sujet.
Quand il s'agit de frameworks, nous parlons de couplage étroit de dépendances. Vous n’avez probablement pas choisi Laravel pour utiliser Doctrine avec ; vous l'avez probablement choisi pour utiliser Eloquent. Il en va de même pour toute fonctionnalité de framework que vous pouvez trouver : vous choisissez le framework en fonction de la rapidité et de la commodité du développement.
Alors, ma question est la suivante : pourquoi ajouteriez-vous une couche de complexité supplémentaire à quelque chose que vous avez choisi pour être simple ?
Attendez, ne me détestez pas pour avoir dit ça. Vous pouvez toujours adapter ou même utiliser une architecture propre avec Laravel, mais l'aspect le plus important de toute décision architecturale est d'avoir une compréhension claire de ce que vous essayez de réaliser ainsi que des avantages et des inconvénients de ces choix.
C'est une décision difficile, et vous pouvez changer d'avis au milieu d'une mise en œuvre. Le logiciel ne devrait pas être immuable, n’est-ce pas ? J'ai un article entier sur la qualité des logiciels dans lequel je lève ce drapeau.
Quoi qu'il en soit, revenons à Laravel et à une architecture propre. L'une des approches les plus ennuyeuses, à mon humble avis, est l'utilisation du modèle Repository avec Eloquent, et c'est ce qui m'a motivé à écrire cet article.
Le modèle de référentiel est un modèle de conception qui sert d'intermédiaire entre les couches de domaine et de mappage de données, agissant comme une collection en mémoire d'objets de domaine. Il vise à découpler les couches de domaine et de mappage de données.
Ainsi, la définition du référentiel est une entité de domaine. Pourquoi? Parce qu'il interagit avec le domaine et définit comment l'infrastructure interagit avec le domaine, agissant comme un contrat entre le domaine et la couche infrastructure de notre application.
Tout d'abord, laissez-moi vous montrer une mauvaise implémentation du modèle Repository que je vois souvent dans les applications Laravel, puis corrigez-le pour vous :
// app/Repositories/UserRepositoryInterface.php <?php namespace App\Repositories; interface UserRepositoryInterface { // ...methods definitions }
// app/Repositories/UserRepository.php <?php namespace App\Repositories; class UserRepository implements UserRepositoryInterface { // ...implementation }
Comme vous pouvez le voir dans le code ci-dessus, le « contrat » avec le domaine et l'infrastructure n'est pas défini à l'intérieur du domaine, mais à l'intérieur de l'infrastructure elle-même. Oui, l'espace de noms de l'application fait partie de la couche d'application et contient tous les éléments non liés au domaine.
Maintenant, voyons comment je mettrais en œuvre la même chose :
// domain/Repositories/UserRepositoryInterface.php <?php namespace Domain\Repositories; interface UserRepositoryInterface { // ...methods definitions }
// app/Repositories/UserRepository.php <?php namespace App\Repositories; use Domain\Repositories\UserRepositoryInterface; class UserRepository implements UserRepositoryInterface { // ...implementation }
Remarquez que nous avons maintenant une couche de domaine appropriée et une couche d'application. C'est assez simple à obtenir, non ?
Maintenant, réfléchissons un peu. Étant donné que l’interface du référentiel fait désormais partie du domaine, cela signifie que nous ne pouvons pas simplement y injecter des dépendances provenant d’autres couches. Voici un mauvais exemple pour l’expliquer :
// app/Repositories/UserRepositoryInterface.php <?php namespace App\Repositories; use App\Models\User; interface UserRepositoryInterface { public function find(int $id): User|null; // ...other methods }
Pas encore clair ? Permettez-moi de le déplacer vers la couche de domaine :
// domain/Repositories/UserRepositoryInterface.php <?php namespace Domain\Repositories; use App\Models\User; interface UserRepositoryInterface { public function find(int $id): User|null; // ...other methods }
Vous voyez le problème ? Nous injectons un modèle Eloquent qui fait partie de la couche d'infrastructure dans notre domaine. Cela couple étroitement notre domaine à Eloquent, ce qui va à l'encontre de l'objectif du modèle Repository.
But how do we solve it? Well, it is not as hard as you might think. You probably already heard about Entities, right? So, let’s fix it:
// domain/Repositories/UserRepositoryInterface.php <?php namespace Domain\Repositories; use Domain\Entities\User; interface UserRepositoryInterface { public function find(int $id): User|null; // ...other methods }
// domain/Entities/User.php <?php namespace Domain\Entities; class User { public function __construct( public int $id; public string $name; public string $email; ) {} // ...other properties and methods }
Our domain layer is now clean from external dependencies, and if we want to move the whole domain to another framework, we can do it.
Now, how do we implement our Repository in our Laravel application? Let me show you:
// app/Repositories/EloquentUserRepository.php <?php namespace App\Repositories; use Domain\Repositories\UserRepositoryInterface; use Domain\Entities\User; use App\Models\User as EloquentUser; class EloquentUserRepository implements UserRepositoryInterface { public function find(int $id): ?User { $eloquentUser = EloquentUser::find($id); return $eloquentUser ? $this->toDomain($eloquentUser): null; } private function toDomain(EloquentUser $eloquentUser): User { return new User( $eloquentUser->id, $eloquentUser->name, $eloquentUser->email ); } }
Now we have a proper implementation of the Repository pattern in Laravel using Eloquent. You can create any other Repository implementation, such as PDOUserRepository.php, DoctrineUserRepository.php, and many others without injecting any dependency into your Domain layer.
Back to what I said in the subject of this article, I’ll complement that, in my humble opinion, using the Repository Pattern with Laravel is just overengineering, and you need a really good reason to do it.
You are adding an extra layer of complexity to your application that you may not need at all, or even worse, it will not guarantee that your business logic is actually isolated into the Domain layer.
What are you trying to achieve here? Think about it. You might end up missing out on many nice functionalities from Laravel or at least making their usage overly complicated.
Are you not sure if Laravel is the best framework for your application and you might move your application to Symfony in the future? Sure, go for the Domain and the Repository. Do you need to replace your SQL database with a NoSQL database later? Maybe it is a good justification. But do you really need it, or is it just charm?
One common argument is that the Repository helps to put some business logic into a separate layer. This normally gives me the creeps because there are better approaches for splitting your logic. Repositories are just for acting as a middle layer to connect the data layer and domain, nothing else. If you want to split your business logic, you should make use of something else — but this is another subject.
In conclusion, while the Repository pattern and clean architecture principles offer significant benefits in terms of maintainability and separation of concerns, their application in a Laravel context often introduces unnecessary complexity. Laravel is designed for simplicity and rapid development, and adding layers of abstraction can complicate this process.
Before implementing the Repository pattern, it is essential to evaluate your project’s specific needs. If decoupling your domain logic from Laravel’s infrastructure is a genuine necessity due to future migrations or a need for flexibility, then the complexity might be warranted. However, for many Laravel applications, leveraging Eloquent directly aligns better with the framework’s strengths and keeps the codebase simpler and more maintainable.
Ultimately, the decision should be driven by a clear understanding of your project’s requirements, balancing the trade-offs involved. Overengineering can lead to more problems than it solves, so aim to keep your solutions as straightforward as possible while still achieving your design goals. The primary objective of any architecture is to solve problems, not to create new ones.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!