Repositories and Decorators

During the Laravel Austin meetup this month, I gave a talk over the repository and decorator pattern, along with a demonstration on how they are useful. First off the repository and decorator pattern is not an official pattern, just the combination of using repositories with the decorator pattern, but "Repository and Decorator Pattern" sounds cooler as a presentation title than "Using repositories with decorators".

Repositories

So what exactly are repositories? Well, repositories allow us to abstract away the database layer. With repositories, we can remove business logic/database logic from your controllers, and prevent our code from becoming coupled to a single database implementation.

Let's create a few repositories, implementing the following interface: Note: The following examples use Laravel specific code, but the concepts can be used anywhere

1<?php
2 
3interface UserRepositoryInterface {
4 public function getAll($limit = 10, $offset = 0);
5}

With this interface, you could create repositories that use Eloquent, Mongo, Redis, Text files... I think you get the point.

1<?php
2 
3class EloquentUserRepository implements UserRepositoryInterface {
4 public function getAll($limit = 10, $offset = 0) {}
5}
6 
7class MongoUserRepository implements UserRepositoryInterface {
8 public function getAll($limit = 10, $offset = 0) {}
9}
10 
11class RedisUserRepository implements UserRepositoryInterface {
12 public function getAll($limit = 10, $offset = 0) {}
13}

Now instead of referencing Eloquent, or the repository directly, lets inject the interface instead. This way, it doesn't matter what implementation we use, as long as it is an instance of UserRepositoryInterface.

1<?php
2 
3class UserController extends Controller {
4 public function __construct(UserRepositoryInterface $user)
5 {
6 $this->users = $users;
7 }
8 
9 public function index()
10 {
11 $users = $this->users->getAll(Input::get('limit', 10), Input::get('offset', 0));
12 
13 return Response::json($users);
14 }
15}

Decorators

So now that we have a basic idea of what repositories are, what about decorators, what are they needed for? Let me pose a hypothetical.

Your application stores users on a MySQL database, and things are running smoothly until you start to notice your application running slow. You notice the slow down is due to constantly looking up users in the database, so what do you do? Add more database servers? Shard your MySQL instance? All of these would work, but they only mask the problem, and expanding horizontally like that begins to add up in server bills. So lets add some caching instead, so that we don't have to hit our database servers as often.

Your first reaction might be to add caching to the UserController around the repository calls, but handling caching really isn't the responsibility of a controller. So, lets edit the EloquentUserRepository directly, and add caching to that. Well, even that has its issues. Every time you touch a class to add functionality, you run the risk of introducing new issues, so how should we do this? That's where decorators come in!

Decorators allow you to wrap classes to extend functionality, without editing the original class. If structured correctly, you can decorate a class over and over again to add new functionality. How do you accomplish this? Well, thats where the UserRepositoryInterface comes in handy, it can be used as a contract to ensure that the decorator implements all the required functions. Lets create a decorator to add caching to our application.

1<?php
2 
3class CachingUserRepository implements UserRepositoryInterface {
4 public function __construct(UserRepositoryInterface $repository, Cache $cache)
5 {
6 $this->repository = $repository;
7 $this->cache = $cache;
8 }
9 
10 public function getAll($limit = 10, $offset = 0)
11 {
12 // Pull the users out of cache, if it exists...
13 return $this->cache->remember('users.all', 60, function() use ($limit, $offset) {
14 // If cache has expired, grab the users out of the database
15 return $this->repository->getAll($limit, $offset);
16 });
17 }
18}

As you can see, we inject an instance of UserRepositoryInterface into the CachingUserRepository class that handles the caching functionality, while using the injected UserRepositoryInterface to load users if the cache has expired. This way we can maintain our seperation of concerns, and keep our code nice and clean. How would this be constructed? Well, when we bind our concrete implementation to our IoC container, it might look something like this:

1<?php
2 
3public function register()
4{
5 $this->app->singleton('UserRepositoryInterface', function() {
6 $eloquentRepo = new EloquentUserRepository(new User);
7 $cachingRepo = new CachingUserRepository($eloquentRepo, $this->app['cache.store']);
8 
9 return $cachingRepo;
10 });
11}

Wrapping up

In closing, this pattern allows you to write clean and highly extendable code, that follows SOLID principles. But, before you jump into using this for every single project you start, remember this is better suited for large applications. Obviously there is nothing wrong with "over engineering" a project for the sake of learning a new concept, but I would slow down a personal weekend project worrying about clean code. Refactoring exists for a reason, and you can always go back later and clean up that weekend project if it grows into something more.

Would you like to learn more about using decorators and repositories? You should check out this video from LaraCasts. A few other videos from LaraCasts about these topics that I highly recommend are: