What is the repository pattern and when should 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.

Lets take this approach (RP) in an example using the language PHP with its one of the best framework called Laravel.

Let’s say you have a model called Post. It retrieve data from the Posts table and you use it in many methods and it is almost in the whole application. 
For now, it retrieves posts by it’s ID, but your boss told you he wants it to retrieve by the post’s name (slug). 
Now, there are two possibilities: your application is not big enough and you can just find and replace every Post::find($id) with something 
like Post::where('slug', $slug)->get(). Perfect, everything works fine!

But what if your app is so big it will just take too much time/stress/whatever to change everything, 
you’re retrieving the post by it’s ID everywhere. What If we could create a layer between the business model and 
the data storing layer - in this case, our controller methods and the database? It would be nice, wouldn’t it? 
It would avoid - between other things - duplicated code, testing, uncentralized code, etc. 

That’s kind of what the repository pattern is about.

Let’s say you want to show a post. You’d usually do something like this:


// app/Http/Controllers/PostsController.php
<?php
public method show($id) {
    $post = Post::find($id);
    return $post;
    // or return a view, whatever.
}
?>
So, it wouldn’t be very easy to mantain this code if things get big. With the Repository Pattern, you could do something like this:
// app/Repositories/PostRepository.php
<?php
namespace App\Repositories;
use App\Post;
class PostRepository
{
    protected $post;
    public function __construct(Post $post)
    {
        $this->post = $post;
    }
    public function find($id)
    {
        return $this->post->find($id);
    }
    public function findBy($att, $column)
    {
        return $this->post->where($att, $column)->get();
    }
}
?>

So, basically, we created a layer and gave it - obviously - a namespace. We created methods to help us retrieve the posts and leave it all in one place. Even better, we could make the class implement an interface, but I’m going to leave this for another post.

Now, we made two methods that allow us to do a lot of things. We can find a post by it’s id, it’s slug, date, name, title, or anything with just two methods. We would need a lot of distributed, different methods through the app to do this. So, now, we just need to use those methods in the PostsController.

<?php
namespace App\Http\Controllers;
use App\Repositories\PostRepository;
class PostsController
{
    protected $post;
    public function __construct(PostRepository $post)
    {
        $this->post = $post;
    }
    public function show($slug)
    {
        return $this->post->findBy('slug', $slug);
    }
}
?>

So, take a look at what we did - first, we made the $post variable protected. Then, we injected the PostRepository dependecy in the constructor (thanks Laravel IoC) and then we just used a function from PostRepository. 
Now we can use one method in multiple places and change how it behaves at multiplace places on just one place - your code is “centralized”, you know where everything is and it’s way easier to mantain.


Above talked about how to create an abstraction layer between the controller and the database. 
We used the following examples: app/Http/Controllers/PostsController.php 
and app/Repositories/PostRepository.php

We learn how to inject the PostRepository class - the one responsible for making the calls to our model - in our PostsController. 
That helped us have more control about our queries, etc. However, it doesn’t give us, for instance, flexibility when changing ORMs. 
To do that, we need to implement an Interface — something that works as a contract to the Class. 
It basically tells the class which methods it should implement without defining how those methods should be treated, click here to learn more —, then, 
we need to make Laravel know which class to use whenever this Interface is implemented and then we simply need to inject it in the controller. 
This way, we can make modifications without having to change anything in the controller. To do that, let’s first create our Interface. 
You may put your file wherever you want, I’ll put it on `app/Repositories/Contracts’.

// app/Repositories/Contracts/PostRepositoryInterface.php
<?php
namespace App\Repositories\Contacts;
interface PostRepositoryInterface
{
    public function find();
    public function findBy($att, $column);
}
?>

Fairly simple: we are simply telling what methods any class that implements PostRepositoryInterface shall have - 
it needs to have these methods, or it’ll throw an exception.

Now, we need to implement this interface in our PostRepository. Our old code was something like this:

<?php
// app/Repositories/PostRepository.php
namespace App\Repositories;
use App\Post;
class PostRepository
{
        protected $post;
       
        public function __construct(Post $post)
        {
                $this->post = $post;
        }
       
        public function find($id)
        {
                return $this->post->find($id);
        }
       
        public function findBy($att, $column)
        {
                return $this->post->where($att, $column)
        }
}
?>

To implement our PostRepositoryInterface, we simply need to use the operator implements in the class declaration. 
We also have to import the Interface. It should look like this:

<?php
// app/Repositories/PostRepository.php
namespace App\Repositories;
use App\Repositories\Contracts\PostRepositoryInterface;
use App\Post;
class PostRepository implements PostRepositoryInterface
{
        protected $post;
       
        public function __construct(Post $post)
        {
                $this->post = $post;
        }
       
        public function find($id)
        {
                return $this->post->find($id);
        }
       
        public function findBy($att, $column)
        {
                return $this->post->where($att, $column)
        }
}
?>

Very simple! Now, If you’d want, you could create another repository — using Doctrine, perhaps — and call it, 
PostRepositoryDoctrine and we’ll have also too implement our interface. 
This way, we would have two (or more) classes (repositories) that implement the same PostRepositoryInterface. 
But how is our controller going to know which class (repository) it should inject? Simple: it doesn’t know. 
Instead of injecting our repository directly in our controller constructor, we’re going to inject our PostRepositoryInterface 
and then we’ll use Laravel’s Service Container to decide which repository (class) use — or, even better, 
make the binding between the interface and the class that should be used. So, first, let’s set this up. To do that, 
open the file app/Providers/AppServiceProvider.php. 
In the register method, we’re going to bind our interface to our correct repository using the bind method. Kinda like this

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('App\Repositories\Contracts\PostRepositoryInterface', 'App\Repositories\PostRepository');
    }
}
?>

We basically told Laravel that, whenever a class needs the PostRepositoryInterface interface, it should inject the PostRepository class. Obviously, we also wrote the namespace of each class.

Now, back to our PostController — we’re going to put our PostRepositoryInterface in our class constructor and then Laravel will “decide” (just as we configured in AppServiceProvider) which class to inject, in our case, PostRepository.


// app/Http/Controllers/PostsController.php
<?php
namespace App\Http\Controllers;
// antigamente: use App\Repositories\PostRepository; agora não precisamos mais disso pois já definimos qual classe injetar quando necessitarmos da interface PostRepositoryInterface. Iremos importá-la e colocá-la no construtor - repare nas linhas 8 e 13
use App\Repositories\Contracts\PostRepositoryInterface;
class PostsController
{
        protected $post;
        public function __construct(PostRepositoryInterface $post)
        {
            $this->post = $post;
        }
        public function show($slug)
        {
            return $this->post->findBy('slug', $slug);
        }
}
?>

In the end, you end up returning an Eloquent Collection, so even if you were going to change your ORM (Doctrine, again, as an example), you’d still have to fix the methods, as you would be returning an array instead of an Eloquent Collection, you could “fix” this by working with arrays everytime, even with Eloquent, but then you’d lose all of it’s magic, wouldn’t you? 
Some would call this over engineering and I consider it too.
Just as explained in the previous post, repositories end up being a way to organize your code — at least for those who use Eloquent. If you’re really interested in using Repositories “the right way” (even when you probably won’t ever stop using Eloquent), it is better to take a look at something like Doctrine.
Thanks!

Thanks : mguimaraes.co

Post a Comment

Post a Comment (0)

Previous Post Next Post