cult3

Implementing The Specification Pattern

Aug 25, 2014

Table of contents:

  1. The problem we are trying to tackle
  2. What is The Specification Pattern?
  3. Why would you choose to use The Specification Pattern
  4. Writing the Username Specification tests
  5. Implementing The Specification pattern
  6. Conclusion

Last week we looked at encapsulating business logic within Value Objects. By using the characteristics of Object Oriented Programming we can maintain the integrity of our business rules within the harmony of using plain old PHP objects.

One of the rules that we needed to implement last week was the uniqueness of a user’s username and email address. This kind of validation can’t fit inside a Value Object because it needs to be able to check the database for existing records.

Instead of coupling the Value Object to the storage mechanism, we can instead use The Specification Pattern to check for uniqueness.

In this week’s tutorial we’ll look at applying The Specification Pattern to encapsulate the business rules of object selection within our applications.

The problem we are trying to tackle

Before I jump into the weeds of explaining The Specification Pattern and implementing it in code, first I’ll briefly clarify the problem we are trying to tackle.

Last week we created an Email Value Object to work with email addresses within Cribbb. We can ensure that email addresses are valid by defining the rule within the constructor of the class:

// This will throw an exception
$email = new Email("not a valid email address");

This is great, but we also need to ensure that the email address hasn’t already been registered.

The uniqueness business rule is very important within the context of our application, but it cannot be satisfied by our simply Value Object because we need to query the data storage to see if the email address already exists.

The Email Value Object does not know anything about data storage, and so we need another way of encapsulating this business rule.

What is The Specification Pattern?

The Specification Pattern is a way of encapsulating business rules to return a boolean value. By encapsulating a business rule within a Specification object, we can create a class that has a single responsibility, but can be combined with other Specification objects to create maintainable rules that can be combined to satisfy complex requirements.

The Specification object has a single public isSatisfiedBy() method that will return a boolean value:

class UsernameIsUnique implements UsernameSpecification
{
    /** @return bool **/
    public function isSatisfiedBy(Username $username)
    {
        //
    }
}

The UsernameIsUnique Specification object can be implemented using whatever means necessary. In this example you would likely inject a repository to query the database to check if the username was indeed unique.

Why would you choose to use The Specification Pattern

The Specification Pattern is powerful because it allows you to encapsulate the business rule inside of the class whilst providing an easy to use API that can be consumed within your application.

By using The Specification Pattern, you can use the Specification Object anywhere that is necessary in your application. Your application does not need to know how the business rule is enforced because it is internal to the Specification object. If the business rules is changed, you only have to change that single source of truth.

Using The Specification Pattern also makes having alternative, or multiple rules easier to work with. By encapsulating each rule as a Specification object you are free to use many instances together to satisfy the complex requirements of the organisation without getting bogged down in complex or unmaintainable code.

Writing the Username Specification tests

The first thing I will do will be to write the tests for UsernameIsUnique specification object.

Create a test file with the following structure:

<?php namespace Cribbb\Domain\Model\Users;

use Mockery as m;

class UsernameIsUniqueTest extends \PHPUnit_Framework_TestCase
{
}

Next write a setUp() method so we don’t have to instantiate a new instance of the object for each test:

public function setUp()
{
    $this->repository = m::mock('Cribbb\Domain\Model\Users\UserRepository');
    $this->spec = new UsernameIsUnique($this->repository);
}

I’m going to be using a repository to query the database so I can inject a mock to satisfy that requirement. Note, the repository doesn’t actually exist yet, but I know it’s going to implement an interface so I can just stick that in for now.

Next I can write my two tests, one where the repository returns null and one where the repository returns a result in order to mock finding an existing user or not:

/** @test */
public function should_return_true_when_unique()
{
    $this->repository->shouldReceive('userByUsername')->andReturn(null);
    $this->assertTrue($this->spec->isSatisfiedBy(new Username('430r0923r0209rjw')));
}

/** @test */
public function should_return_false_when_not_unique()
{
    $this->repository->shouldReceive('userByUsername')->andReturn(['id' => 1]);
    $this->assertFalse($this->spec->isSatisfiedBy(new Username('hello_world')));
}

Now if you give phpunit a blast you should see those tests failing.

Implementing The Specification pattern

The first thing I will do to implement The Specification pattern will be to create a UsernameSpecification interface:

<?php namespace Cribbb\Domain\Model\Users;

interface UsernameSpecification
{
    /**
     * Check to see if the specification is satisfied
     *
     * @return bool
     */
    public function isSatisfiedBy(Username $username);
}

We’re probably going to need multiple specification objects and so it’s good practice to define an interface so that each implementation abides by the same contract.

Next we can create the UsernameIsUnique specification object:

<?php namespace Cribbb\Domain\Model\Users;

class UsernameIsUnique implements UsernameSpecification
{
    /**
     * @var UserRepository
     */
    private $repository;

    /**
     * Create a new instance of the UsernameIsUnique specification
     *
     * @param UserRepository $repository
     */
    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    /**
     * Check to see if the specification is satisfied
     *
     * @param Username $username
     * @return bool
     */
    public function isSatisfiedBy(Username $username)
    {
        if ($this->repository->userByUsername($username)) {
            return false;
        }

        return true;
    }
}

In this class I’m injecting a repository that implements the UserRepository interface and then setting it as a property of the class.

Next I’m implementing the isSatisfied() method by searching the database for a user that has that username. If a record is returned we can return false because the username is not unique, otherwise we can return true.

Now if you run the tests again they should all pass!

As with last week’s tutorial, I won’t go into how to implement the code to check if an email is unique as it is essentially exactly the same as the code in this tutorial. If you get stuck you can always check my GitHub repository.

Conclusion

In today’s tutorial we’ve looked at how to encapsulate the business rules of object selection within our applications. Instead of coupling objects together, or adding extra responsibility to existing classes, we can create single responsibility specification objects that can be combined together to give us the flexibility to meet complex and critical business rules.

As with last week’s post, this tutorial was heavily inspired by the excellent talk Unbreakable Domain Models by Mathias Verraes. If you haven’t already watched this talk, I highly recommend you do so today!

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.