When to use Repository vs Service vs Trait in Laravel? [closed]
Traits
Are an alternative approach to inheritance that solves some limitations of single class inheritance, which PHP
uses. This is commonly used to share similar logic across models. Let's imagine a couple of models have a Company relationship.
trait HasCompany { public function company() { return $this->belongsTo(Company::class); }}
Now the user can easily share code from the trait, by the keyword using
. This is an example and a more complex use case would be needed for it to make sense.
class User { use HasCompany;}
Repositories
Repositories is a design pattern to abstract data layers from the application. Your logic should not care about how you store data, so if you wanted to change from Mysql
to Mongodb
, you would only swap out the repository and not have to change business logic.
Very opinionated here, but this is not a fitting design pattern for Laravel
. Laravel
has Eloquent and the database layer is therefor already abstracted. Repositories is sometimes used for Laravel
applications, but rather an outlier, than a common sight. One of the main reasons for repositories, is to be data implementation agnostic, which already exists and you can swap between SQL servers flawlessly. Also Eloquents
features like ::find()
, scopes
etc. feels like a replacement for Repositories and is quirky to use at the same time.
If you use Doctrine as the ORM
, which you can in Laravel
, it is the core of their architecture and should be used.
Services
Is commonly used for a place to store business logic or the building blocks of your actions in your application. In traditional MVC
design, Controllers should only handle input. Normally you would put your logic in Models, but they get "fat" very quickly, when this happens services is a common place to put business logic. Sometimes also named actions or commands, which is similar but a little bit different approaches.
One of the core things it solves, is to make your business logic reusable. Imaging filtering all users by an active flag, when you retrieve it in its controller.
public function all() { return User::where('active', true)->get();}
Now you have your business logic, that enforces that you only work on active users, later you want to notify all active users with an email, by notifications using a command.
class NotifyUsers extends Command { public function handle() { foreach (User::where('active', true)->get() as $user) { $user->notify(); } }}
Now you manually have to keep business logic up to date, next time you add a second condition or similar, therefor having to change the code in two places. In a big application it can make it quite hard to maintain the conditions in multiple places. If you make a service with this logic. You can utilize the same business logic across the application and only have to maintain it in one place.
class UserService { public function all() { return User::where('active', true)->get(); }}
Everywhere you want to use this business logic getting active users, you can use the service. Therefor only having one place to maintain the logic. A call can be as simply as resolve(UserService::class)->all()
. Example of the updated logic with services would be.
// controllerpublic function all(UserService $userService) { return $userService->all();}// commandclass NotifyUsers extends Command { public function handle(UserService $userService) { $userService->all()->each->notify(); }}
Conclusion
The world is not black and white, you have to figure out your own approach. My advice is, do not spend time on Repositories, Laravel
has a lot of features to handle data related operations scopes
, getters
setters
etc. that conflicts with the Repository design pattern. See if a service like design approach suits you and you can utilize em. Traits
is not related to services and repositories, it is just a tool similar to Class inheritance, to share logic between objects.