Repository Pattern

The repository pattern is one of the most useful design patterns that can be used in every software architecture. Every application always have to work with persistence in some way. To handle this we can use this pattern.

What is a repository?

Repositories represent a collection of entities. It does not represent the database storage or caching or any technical concerns related to that. So to be clear, repositories represents collections. How the data is hold in those repositories is an implementation detail. This is a very important concept when working with repositories.

An Example

Lets assume we have an Order entity representing an order and and Order Repository which is a collection of orders.  You then create a new Order and add it to this repository.  If you then want to get back all orders from the collection you will also get back the order you just added. You can also get back one order based on an id. You need to think the repository as being a collection of data arrays. This allows us to add or remove items to or from this data array. And also to search in this array.

So to create a repository we first have to define an interface. This is our boundary between the persistence layer and rest of the application. You always need to go trough the interface to get an implementation. It also represents our contract for the implementations. Every implementation needs to have these public methods.

interface OrderRepositoryInterface
{
    public function findById(int $orderId): Order;
    public function findAll(): array;
    public function save(Order $order): bool;
}

Here we have a simple in memory implementation. But we can also implement a MySQL or doctrine repository if we like to.

class InMemoryOrderRepository implements OrderRepositoryInterface
{
    private $orders = [];

    public function findById(int $orderId): Order
    {
        if (!isset($this->orders[$orderId])) {
            return null;
        }
        return $this->orders[$orderId];
    }

    public function findAll(): array
    {
        return $this->orders;
    }

    public function save(Order $order): bool
    {
        $this->orders[$order->getId()] = $order;
    }
}

Our application will only know about the interface. The implementation of our repository is completely decoupled from the rest of the application. This means our persistence implementation is just a detail to the rest of the application. If we change our database engine, we only need to change the repository implementations without affecting anything else in our application.

Should a repository create entities?

The short answers is no. We said that a repository is a collection. Its not a collections job to create entities, that is the job for factories. We should always follow the single responsibility principle. Its a factories job to put together complex objects. Its the repositories single responsibility to be a collection of entities. If we need to change our collections, then we change our repository. If we need to change our entity creation, then we change our factory. This is important to remember.

Why should you use a repository?

Abstraction

A repository allows us to abstract our data storage. This allows us to make it a detail. We don’t need to put our specialized queries directly in the code. This will make our code completely coupled to our persistence layer, and that’s not something you want. We can also start working on the business logic without worrying about the persistence implementation. You can start with a simple in memory implementation and pull in a database engine or solution as the last step. You should always delay choices as long as possible. Then you have more information about what the application requires and you can make the best choice possible.

Freedom to change data storage

You are free to change the repository implementations but are you really going to do that? Honestly its a weak reason. No-one would use repositories if this was the only good reason. But still its a reason. And in case you want to do it, its possible and easier then if your persistence code was all over your code.

Testability

A repository is easy to test. Because your persistence is completely decoupled we can easily unit test this part. This allows us to have easy and good tested code.

Seperation of concerns

In a good software architecture you should be able to separate business logic from persistence. What this allows you do to is to split your architecture in layers. You can have a presentation, service and persistence layer if you want to. I will talk more about different kind of architecture’s in future blog posts. But the repository pattern is always an important part of it. Its crucial in most good software architectures.

Collection vs persistent-oriented

There are 2 types of repositories. You have collection-oriented and persistence-oriented repositories. The idea of a collection-oriented repository is that the repository should be treated as an in memory array store. A persistence-oriented repository comes with an assumption that there already is a deeper storage going on. The difference here is in how you name your methods.

// Collection-oriented
$orderRepository->add($order);

// Persistence-oriented
$orderRepository->save($order);

Which one you use is up to you. Currently I’m more inclined to use persistence-oriented because I look at it more as persistence. But in the end you should be flexible about this, but consistence. Decide with your team what to use, and stick with it. There is no good or bad here. There is only a choice to make.

Conclusion

So its important to give repositories the single responsibility to act as a collection and not use it to create new entities, we use factories for that. A repository is a way to decouple persistence from the rest of our applications and allows us to make it a detail. We can decouple any business logic from persistence details and keep its implementation until the last moment. The repository pattern is important in any good software architecture and its important to understand this. And remember, always think to code! Know your patterns.