Home >Backend Development >PHP Tutorial >Your Laravel application with Repository doesnt make any sense
I’ve seen over the years many developers using the Repository pattern with Laravel, attempting to apply clean architecture concepts to their applications, but often misunderstanding the general concept of using a framework like Laravel.
Before I start and have to dodge some stones that I know you folks might throw my way, let me be clear: this is my opinion as a software engineer who has worked with several languages, frameworks, building software from scratch, and maintaining old legacy codebases. My word is not final, and as always, I believe the most acceptable answer to any software engineering question is: “It depends on what you are willing to trade off to solve your problem.”
So, without further ado, let’s get into the subject.
Clean Architecture is a software design philosophy that aims to create systems that are easy to maintain, test, and understand. It emphasizes the separation of concerns and the creation of boundaries between different parts of the system to ensure that changes in one part do not adversely affect others. This architecture was popularized by Robert C. Martin (Uncle Bob) and is often used to guide the organization of code in a way that is resilient to changes in business rules or technical requirements.
In short, what Bob proposes is that your application should be split into layers that follow key principles to allow your business logic to be decoupled from any external dependencies, giving them mobility and a single responsibility context. But this isn’t going to be a clean architecture article; I just wanted to put us on the same page about it.
When it comes to frameworks, we’re talking about tight coupling of dependencies. You likely didn’t pick Laravel to use Doctrine with it; you probably chose it to use Eloquent. The same goes for any piece of framework functionality you may find — you are picking the framework for development speed and convenience.
So, my question is: Why would you add an extra complexity layer to something that you picked to be simple?
Wait, don’t hate me for saying this. You can still adapt or even use clean architecture with Laravel, but the most important aspect of any architectural decision is having a clear understanding of what you are trying to achieve and the pros and cons of those choices.
It’s a hard decision, and you can change your mind in the middle of an implementation. Software should not be immutable, right? I have a whole article about software quality where I raise this flag.
Anyway, getting back to Laravel and clean architecture. One of the most annoying approaches, in my humble opinion, is the use of the Repository pattern with Eloquent, and this is what motivated me to write this article.
The Repository pattern is a design pattern that mediates between the domain and data mapping layers, acting as an in-memory collection of domain objects. It aims to decouple the domain and data mapping layers.
So, the Repository definition is a domain entity. Why? Because it interacts with the domain and defines how the infrastructure interacts with the domain, acting as a contract between the Domain and the Infrastructure layer of our application.
First, let me show you a bad implementation of the Repository pattern that I often see in Laravel applications and then fix it for you:
// 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 }
As you can see in the code above, the “contract” with the domain and the infrastructure isn’t defined inside the domain, but inside the infrastructure itself. Yes, the App namespace is part of the Application Layer, and it contains all the non-domain stuff.
Now, let’s see how I would implement the same thing:
// 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 }
Notice that now we have a proper domain layer and an application layer. It is quite simple to get, right?
Now, let us think a bit. Since the Repository Interface is now part of the Domain, it means that we can’t simply inject dependencies from other layers into it. Here’s a bad example to explain it:
// app/Repositories/UserRepositoryInterface.php <?php namespace App\Repositories; use App\Models\User; interface UserRepositoryInterface { public function find(int $id): User|null; // ...other methods }
Not clear yet? Let me move it to the domain layer:
// domain/Repositories/UserRepositoryInterface.php <?php namespace Domain\Repositories; use App\Models\User; interface UserRepositoryInterface { public function find(int $id): User|null; // ...other methods }
See the problem? We’re injecting an Eloquent model that is part of the infrastructure layer into our domain. This tightly couples our domain to Eloquent, which defeats the purpose of the Repository pattern.
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.
The above is the detailed content of Your Laravel application with Repository doesnt make any sense. For more information, please follow other related articles on the PHP Chinese website!