There are countless tutorials available online explaining how to apply the Repository Pattern in your Laravel application, and you may, unfortunately, even see the pattern referenced as a ‘Laravel best practice’. The vast majority of these articles cite the benefits as being along the lines of:
…using repositories will make our controllers a bit less verbose, more loosely coupled, and easier to read —Vegibit
…[repositories provide] flexibility when changing ORM — Mateus Guimarães
These sound like admirable goals, but are sadly left unachieved by the tutorials which exist on the first page of a Google search on this topic. Let’s look at why this is the case and how we can fix it.
What is the repository pattern and when should you use it?
The Repository Pattern is key pattern in Domain Driven Design (DDD), but it can of course be applied if you’re not strictly following DDD. It is not a Laravel specific pattern. In Martin Fowler’s Patterns of Enterprise Application Architecture, the following definition of a repository is given:
[A Repository] mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects — Patterns of Enterprise Application Architecture by Martin Fowler
A repository is just an intermediary that abstracts away the domain object persistence details and complexities from the other parts of your application.
Note: There exists several Stack Overflow questions along the lines of ORM vs Repositories. Do not confuse the concept of an ORM with a repository! They are not the same thing.
So now we understand what the pattern is about, we should address when to use it. Fortunately, this article by MSDN gives us a list of use cases for the Repository Pattern! One of the most important of these cases, particularly in the context of the aforementioned Laravel tutorials, is when you want to centralise and encapsulate reusable data access queries which will improve the maintainability and readability of your code. When you look at common implementations of this pattern, you’ll see that many repositories end up as encapsulations of ORM query builder usage. This is where many Laravel based tutorials misunderstand the repository pattern and return the result of Eloquent queries, making their repository untrue to the definition we gave earlier.
If my repository encapsulates and ‘hides’ my Eloquent queries from the rest of my application, surely I’ve decoupled my code from Eloquent?
Unfortunately this supposition is invalid, and the problem probably lies in your interface definition. To illustrate this, let’s first look at the classic Blog example. You’ll often see the following repository in one form or another:
So whenever we want to retrieve the active posts for displaying on our web frontend, or for sending out weekly blog digests for example, we have a nice clean query that encapsulates our query logic. But if we assume this implementation satisfies the PostRepositoryInterface, any calling code that uses the NaiveEloquentPostRepository will be coupled to Eloquent, even if it type hints the PostRepositoryInterface and let’s the container resolve the specific implementation.
As proof, consider the scenario where tomorrow you decide to remove Eloquent in favour of some other ORM. What happens next? Well first you’d probably write a new repository that implements the PostRepositoryInterface, and you’d assume the rest of your application will continue to function if you’ve decoupled yourself successfully. But it won’t because your application is expecting Eloquent models rather than domain specific objects. This will most likely be demonstrated by the repository calling code accessing properties on the Eloquent objects as if they were public properties, as is the Eloquent way, rather than using getter and setter methods.
So how do I decouple myself from Eloquent?
If you dig around Laravel forums enough, you’ll probably see someone asking this very question and the most common answer given is
Well if you want full decoupling from Eloquent, then you should probably use the toArray() method on the Eloquent collection when returning the result of your query
Oh dear. The solution is definitely not to just dump the collection or model to an array. That’s just madness! Let’s not abandon Object Oriented Programming altogether every time we’re confronted with an issue otherwise things could get messy, really fast.
The solution is easy and is in fact implied in the definition of a Repository referenced earlier in this article. A repository is supposed to provide a collection-like interface for access to domain objects, therefore your interface methods should declare that they will return domain objects. Looking back at our NaiveEloquentPostRepository implementation, we would then need to map the Eloquent model(s) to domain objects using, for example, some sort of mapper class and then return these objects from the repository. That way, if you decide you want to rip out Eloquent, the rest of your application will be none the wiser since they only care about the domain objects and never see or use Eloquent models.
If you are returning Eloquent objects from your repositories, your application is theoretically coupled to Eloquent. But before you start thinking about creating separate domain objects and mapping between them and Eloquent models, ask yourself is it worth it? Are you really going to move away from Eloquent? I’ve only ever changed ORM once or twice and that was during very early prototyping when I felt like playing around with different technologies.
If you’re writing a tutorial, you need be clear with readers what they will get out of it. If the goal is to demonstrate how the repository pattern can provide ORM decoupling, then you need to implement it correctly. Implementing what is effectively an Eloquent Query Abstraction Layer as I like to call it, is useful but will not achieve this goal. For me, an Eloquent Query Abstraction Layer is a way of encapsulating query logic for reuse whilst retaining all the good stuff that you can do with Eloquent models (which is a lot!). It’s always important when using design patterns to understand when to use them and how they apply to the problem you’re facing. Make design patterns work for you, but understand the background of the pattern and the problem it’s trying to solve.
In my opinion, Laravel works so well out of the box with Eloquent that if you do decide you’ve fallen out of love with Eloquent at some point in the future, you’ll probably find you’ve fallen out of love with Laravel as well. Because of the way Laravel and an Active Record system like Eloquent work together, I believe the repository pattern in it’s true sense isn’t worth the effort in most Laravel applications. However, there is value in using an Eloquent Query Abstraction Layer that is described in the tutorials mentioned in the introduction of this article. It is important to remember that as a community, we need to be wary of misusing fundamental terminology and concepts as it will leave many newcomers to the Laravel world confused or frustrated.
On a side note, I would be interested to hear in the comments about scenarios where people have found themselves needing to switch out an ORM for an existing application!