Repository Design Pattern Demystified

Share this article

What is the Repository Design Pattern?

To put it simply, it is an implementation of a brokering layer between the application and a data source. Neither party needs to be be aware of the other to perform their respective jobs which allows us to have a decoupled architecture which in turn helps in the scaling of the application in the big leagues without having hard dependencies.

Why should you care?

Let us understand this with an example. Imagine we are building an online store which sells orange flavored candies. It’s a small store and it keeps local inventory, so we don’t need anything fancy here. The storefront application can just hook into the database and take orders online based on how much inventory is at hand. This will work fine since the store has only one supply warehouse and has a limited area of operation. But what will happen if this store wants to expand its area of operation? The store might want to expand into another city or across the country and having a central inventory system would be so cumbersome.

Now if we are still using data models then we have a somewhat tightly coupled application. The storefront application needs to be aware of every data source it has to interact with and that is a poor application design. The job of the storefront application here is to allow customers to place orders for the candy, the application should not be concerned about the data source, it should not have to keep track of all the different data sources. This is where data repositories come in to play. Per the Repository Design Pattern, a public API is exposed via an interface and every consumer (our storefront application in this case) uses that API to talk to the data source. Which data source is being used or how its being connected to, these are not of concern to the application. The application is only concerned with the data it gets and the data it sends to be saved.

Once the Repository Design Pattern is implemented, repositories can be created for each data source. The storefront application no longer would need to keep track of any data source, it just uses the repository API to get the data it needs.

Is it the magic bullet?

Well, no it is not. Like every design pattern it has its ups and downs, pros and cons.

Pros:

  • Separation of concerns; the application need not know about or track any or all data sources.
  • Allows easy unit testing as the repositories are bound to interfaces which are injected into classes at run time.
  • DRY (Dont Repeat Yourself) design, the code to query and fetch data from data source(s) is not repeated.

Cons:

  • Adds another layer of abstraction which adds a certain level of complexity making it an overkill for small applications.

How to go about it?

Let us see this with a little code example. I will use Laravel here in the example to leverage its excellent dependency injection feature. If you use any modern PHP framework then it should already have Dependency Injection/IoC container. Dependency Injection is required to implement Repository Design Pattern because without it you will not be able to bind a data repository to the repository interface and the whole idea is to code to an interface to avoid hard coupling. If you are not using any framework or your choice of framework does not have an IoC container then you can use an off the shelf IoC container (check footnotes).

Let’s crack on. Firstly, we set up our namespace and autoloading in Composer. Open up composer.json and add psr-4 autoloading for our namespace (in autoload node right after classmap).

    "autoload": {
        "classmap": [
            "app/commands",
            "app/controllers",
            "app/models",
            "app/database/migrations",
            "app/database/seeds",
            "app/tests/TestCase.php"
        ],
        "psr-4": {
            "RocketCandy\\": "app/RocketCandy"
        }
    },

After saving it, execute composer dump-autoload -o in the terminal to register autoloading for the new namespace. Create OrangeCandyRepository.php in app/RocketCandy/Repositories/OrangeCandyRepository/. This will be our repository interface.

<?php

namespace RocketCandy\Repositories\OrangeCandyRepository;

interface OrangeCandyRepository {

    public function get_list( $limit = 0, $skip = 0 );

    public function get_detail( $candy_id = 0 );

}

Now that we have an interface, we can create a repository. Create CityAOrangeCandyRepository.php in app/RocketCandy/Repositories/OrangeCandyRepository/.

<?php

namespace RocketCandy\Repositories\OrangeCandyRepository;

class CityAOrangeCandyRepository implements OrangeCandyRepository {

    public function get_list( $limit = 0, $skip = 0 ) {
        //query the data source and get the list of
        //candies
    }

    public function get_detail( $candy_id = 0 ) {
        //query the data source and get the details of
        //candies
    }

}

To bind CityAOrangeCandyRepository repository to the OrangeCandyRepository interface, we will leverage Laravel’s IoC container. Open up app/start/global.php and add following to the end of the file.

//OrangeCandyRepository
App::bind(
    'RocketCandy\Repositories\OrangeCandyRepository\OrangeCandyRepository',
    'RocketCandy\Repositories\OrangeCandyRepository\CityAOrangeCandyRepository'
);

Note: I have put the IoC binding in global.php only for demonstration. Ideally these should be put into a separate file of their own where you would put all the IoC bindings and load up that file here in global.php or you would create Service Providers to register each IoC binding. You can read more here.

Now we can go ahead and make use of the repository via the interface. In our CandyListingController.php located in app/controllers/.

<?php

use RocketCandy\Repositories\OrangeCandyRepository\OrangeCandyRepository;

class CandyListingController extends BaseController {

    /**
     * @var RocketCandy\Repositories\OrangeCandyRepository\OrangeCandyRepository
     */
    protected $_orange_candy;

    public function __construct( OrangeCandyRepository $orange_candy ) {
        $this->_orange_candy = $orange_candy;
    }

}

Here we inject the OrangeCandyRepository interface into our controller and store its object reference in a class variable which can now be used by any function in the controller to query data. Since we bound OrangeCandyRepository interface to the CityAOrangeCandyRepository repository, it will be as if we are directly using CityAOrangeCandyRepository repository itself.

So now, the type and kind of data source is the sole concern of CityAOrangeCandyRepository here. Our application knows only of OrangeCandyRepository interface and the API it exposes to which every repository implementing it must adhere to. The repository is resolved out of IoC container at run time, which means the interface <=> repository binding can be set as needed, the interface can be bound to any data repository and our application would not have to be concerned about the change in data source which can now be a database or a web service or a trans-dimensional hyper-data conduit.

One size does not fit all

As I mentioned above in the Cons of Repository Design Pattern, it adds a bit of complexity to the application. So if you are making a small application and you do not see it graduating to big leagues where more than one data source might be called into service, you will be better off not implementing this and sticking to good old data models. Knowing something is different than knowing when to use that thing. It is a very handy design pattern that saves a lot of headache both when creating an application and when that application has to be maintained or scaled up (or down) but it is not a magic bullet for every application.

I used Laravel specific code to demonstrate the implementation above, but it is fairly simple and similar with any decent IoC container. Got questions? Fire away in the comments below.

Footnotes:

Frequently Asked Questions about the Repository Design Pattern

What is the main purpose of the Repository Design Pattern?

The Repository Design Pattern is a design pattern that mediates between the domain and data mapping layers of an application. It provides a simple way to achieve separation of concerns by decoupling the infrastructure or technology used to access databases from the domain model layer. This allows the application to be more maintainable, testable, and modular, as changes in the database access technology will not affect the domain model layer.

How does the Repository Design Pattern work in PHP?

In PHP, the Repository Design Pattern works by creating an interface that defines the methods that will be used to access the data source. Then, a concrete class is created that implements this interface and provides the actual implementation of these methods. This class will handle the actual data retrieval and storage operations, while the interface ensures that the rest of the application is not dependent on these details.

Can the Repository Design Pattern be used with other design patterns?

Yes, the Repository Design Pattern can be used in conjunction with other design patterns. For example, it can be used with the Unit of Work pattern to handle transactions, or with the Factory pattern to create domain objects. This allows for a more flexible and robust architecture, as each pattern can handle a specific concern.

What are the benefits of using the Repository Design Pattern in a microservices architecture?

In a microservices architecture, the Repository Design Pattern can provide several benefits. It can help to isolate the data access details from the rest of the application, making it easier to change the data source or implement different strategies for data access. It can also help to reduce code duplication, as the same repository can be used by different services.

How does the Repository Design Pattern relate to Domain-Driven Design (DDD)?

The Repository Design Pattern is a key part of Domain-Driven Design (DDD). In DDD, the repository provides a collection-like interface for accessing domain objects. This allows the domain layer to be agnostic about the details of data storage and retrieval, which can be handled by the repository.

What are some common pitfalls when implementing the Repository Design Pattern?

Some common pitfalls when implementing the Repository Design Pattern include overcomplicating the repository interface, not properly handling exceptions, and not considering concurrency issues. It’s important to keep the repository interface as simple as possible, and to ensure that any exceptions are handled in a way that makes sense for the application.

How can I test a repository?

Testing a repository typically involves creating unit tests for each method in the repository interface. These tests should verify that the repository correctly interacts with the data source and returns the expected results. It’s also important to test how the repository handles exceptions and edge cases.

Can the Repository Design Pattern be used in a NoSQL context?

Yes, the Repository Design Pattern can be used in a NoSQL context. The pattern is agnostic about the type of data source, so it can be used with any type of database, including NoSQL databases. The implementation of the repository will differ depending on the specific type of NoSQL database being used.

How does the Repository Design Pattern handle transactions?

The Repository Design Pattern itself does not handle transactions. However, it can be used in conjunction with the Unit of Work pattern to handle transactions. The Unit of Work pattern manages transactions by keeping track of all the objects that need to be saved or updated in a transaction, and then coordinating the writing of changes to the database.

How can the Repository Design Pattern improve the performance of an application?

The Repository Design Pattern can improve the performance of an application by providing a layer of abstraction over the data source. This can allow for optimizations such as caching, lazy loading, and batching of operations, which can significantly improve the performance of data access operations.

Amit GuptaAmit Gupta
View Author

Amit is a web applications engineer and a BarCamper based in India. He defected from Java to camp PHP a decade ago and never had the urge to go back. Besides building new applications, he likes photography and is always keen to learn new stuff. Amit loves to talk about application engineering and photography. He can be found blogging now & then on his dev blog or rambling on Twitter.

ddddesign patterndomain driven designmysqlPHPrepository
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week