cult3

The User Entity and The Ubiquitous Language

Sep 01, 2014

Table of contents:

  1. Creating a new User
  2. What is the Ubiquitous Language and how can we use it?
  3. Using UUIDs instead of Auto-incrementing IDs
  4. Creating a UserId class
  5. Writing the User tests
  6. Creating the User entity
  7. Conclusion

Over the last couple of weeks we’ve looked at using the power of Object Oriented Programming to encapsulate the business rules of our application.

By defining Value Objects such as Email, Username and Password we can ensure that the objects we are working within our application follow the explicit rules of the business.

Next we looked at The Specification Pattern. The Specification Pattern allows us to encapsulate business rules around the selection of objects within the application.. This enables us to create reusable classes that can be mixed and matched to satisfy the complex requirements of the application we are building.

Today I want to look at using these components to build the User entity. The User entity is going to be the main class that we use for identity within Cribbb. I also want to take a first look at the Ubiquitous language of the project and how that will dictate how we code.

Creating a new User

Virtually all applications have some form of identity or authentication. If this is not your first time at the rodeo you are probably well versed in creating something along the lines of a User entity.

Typically you would write the following code to create a new user in your application:

$user = new User();
$user->save();

Perhaps you might set the user’s name and email address:

$user = new User();
$user->name = "Philip Brown";
$user->email = "name@domain.com";
$user->save();

Or perhaps you would create and save the user all in one go:

$user = User::create([
    "name" => "Philip Brown",
    "email" => "name@domain.com",
]);

The three methods above are totally legitimate ways of creating a new user in an application.

However, if you remember back to Encapsulating your application’s business rules, one of our business rules is “all users must have a email, username and password”.

How can we enforce that a user must have an email, username and password?

Well, the best way of doing that is also the simplest. We can enforce that a user has an email, username and password by expecting them to be passed in through the __construct() method:

class User {

    public function __construct(Email $email, Username $username, Password $password)
    {
        $this->email = $email;
        $this->username = $username;
        $this->password = $password;
    }
}

$user = new User(
    new Email('name@domain.com'),
    new Username('username'),
    new Password('qwerty')
);

Now we can be certain that whenever a new User object is created within our application, it will require an Email, Username and Password to be passed in as arguments.

What is the Ubiquitous Language and how can we use it?

Ubiquitous Language is a term from the world of Domain Driven Design. Basically it means the language that is used by the “business experts” to describe the application.

When there are two distinct groups working on an application, developers on one side and business people on the other, a lot of the meaning and nuance of an application can get lost in translation.

However, when everyone agrees on a Ubiquitous Language to talk about the project, and developers use that language in their work, the project is better equipped to stay on track.

In the example above we are saying we create a new user:

$user = new User(
    new Email("name@domain.com"),
    new Username("username"),
    new Password("qwerty")
);

However, do we really create a user? No, a user registers with our application. Instead of “creating” a user we should be using the terms from our Ubiquitous Language:

$user = User::register(
    new Email("name@domain.com"),
    new Username("username"),
    new Password("qwerty")
);

In the example above I’ve simply made the __construct method private and created a static register method.

To read more on this topic I highly recommend reading Named Constructors in PHP.

Using UUIDs instead of Auto-incrementing IDs

A final thing I want to look at before we actually create the User entity is using UUIDs instead of auto-incrementing ids.

You are probably already aware of what auto-incrementing ids are. Basically an auto-incrementing id is the primary key of the record that is set when you create a new record in the database.

A UUID (Universally unique identifier) is an id that is generated by the application, rather than the database.

UUIDs are beneficial for a number of reasons. Firstly, you are not relying on the infrastructure to generate ids for you. The infrastructure shouldn’t really leak into your application as the database is just an implementation detail.

Secondly, UUIDs are great if you plan to distribute your infrastructure. Using UUIDs instead of auto-incrementing ids will make it dramatically easier to partition your databases if you are lucky in enough to reach that type of scale.

The downsides to using UUIDs are, you can’t easily determine the order of records based on their id, and UUIDs are going to be bigger than auto-incrementing ids. Jeff Atwood has a nice post on using IDs versus GUIDs

PHP does not have a native UUID class, and so I’m going to be using this package. As I mentioned in Encapsulating your application’s business rules, I don’t mind using these types of packages in my domain related code because if PHP had a native class I would use it, and the package has no third-party dependencies to worry about.

Creating a UserId class

In order to represent my User entity id, I’m going to create a new UserId class. This means that throughout my application I can type hint for the UserId class to ensure I’ve got an id of the correct type.

First I will write my tests for the UserId class I intend to write. We’re keeping this pretty basic for now:

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

use Rhumsaa\Uuid\Uuid;

class UserIdTest extends \PHPUnit_Framework_TestCase
{
    /** @test */
    public function should_require_instance_of_uuid()
    {
        $this->setExpectedException("Exception");

        $id = new UserId();
    }

    /** @test */
    public function should_create_new_user_id()
    {
        $id = new UserId(Uuid::uuid4());

        $this->assertInstanceOf("Cribbb\Domain\Model\Users\UserId", $id);
    }

    /** @test */
    public function should_create_user_id_from_string()
    {
        $id = UserId::fromString("d16f9fe7-e947-460e-99f6-2d64d65f46bc");

        $this->assertInstanceOf("Cribbb\Domain\Model\Users\UserId", $id);
    }
}

In the tests above I’m simply making sure the UserId class is being instantiated correctly. I’ve also wrote a fromString() method to take string UUIDs and convert them into instances of UserId. This will be useful for when I’m getting records from the database.

The implementation of this class looks like this:

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

use Rhumsaa\Uuid\Uuid;

class UserId
{
    /**
     * @var Uuid
     */
    private $value;

    /**
     * Create a new Uuid instance
     *
     * @return void
     */
    public function __construct(Uuid $value)
    {
        $this->value = $value;
    }

    /**
     * Create a UserId from a string
     *
     * @param string $userId
     * @return UserId
     */
    public static function fromString($userId)
    {
        return new UserId(Uuid::fromString($userId));
    }

    /**
     * Return the object as a string
     *
     * @return string
     */
    public function __toString()
    {
        return $this->value->toString();
    }
}

If you have been following a long with this series, the code above should look pretty familiar by now.

Writing the User tests

Now that I’ve got the UserId class set up I can write the tests to ensure that a new User object is instantiated correctly.

First I will set the test file up:

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

use Rhumsaa\Uuid\Uuid;

class UserTest extends \PHPUnit_Framework_TestCase
{
}

Next I will write a setUp() method so I don’t have to repeat the same code for each test:

/** @var UserId */
private $userId;

/** @var Email */
private $email;

/** @var Username */
private $username;

/** @var HashedPassword */
private $password;

public function setUp()
{
    $this->userId = new UserId(Uuid::uuid4());
    $this->email = new Email('name@domain.com');
    $this->username = new Username('my_username');
    $this->password = new Password('super_secret_password');
}

Next I can write the tests to ensure an Exception is thrown if any of the dependencies are missing:

/** @test */
public function should_require_user_id()
{
    $this->setExpectedException('Exception');
    $user = User::register(null, $this->email, $this->username, $this->password);
}

/** @test */
public function should_require_email()
{
    $this->setExpectedException('Exception');
    $user = User::register($this->userId, null, $this->username, $this->password);
}

/** @test */
public function should_require_username()
{
    $this->setExpectedException('Exception');
    $user = User::register($this->userId, $this->email, null, $this->password);
}

/** @test */
public function should_require_password()
{
    $this->setExpectedException('Exception');
    $user = User::register($this->userId, $this->email, $this->username, null);
}

And finally I will write a test to ensure that a new User is created correctly:

/** @test */
public function should_create_new_user()
{
    $user = User::register($this->userId, $this->email, $this->username, $this->password);

    $this->assertInstanceOf('Cribbb\Domain\Model\Users\User', $user);
    $this->assertInstanceOf('Cribbb\Domain\Model\Users\UserId', $user->id());
    $this->assertEquals($user->email(), 'name@domain.com');
    $this->assertEquals($user->username(), 'my_username');
}

With the tests in place, let’s take a look at how to implement the User entity.

Creating the User entity

Create a new file called User.php under the Users folder from last week and copy the following code:

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

class User
{
}

Although we have covered Doctrine 2 Entities already, we can basically just ignore the database for now because it isn’t really important.

Next we can define the class properties we are going to set:

private $id;

private $email;

private $username;

private $password;

And we can write the __construct() method to accept the dependencies:

/**
 * Create a new User
 *
 * @param UserId $userId
 * @param Email $email
 * @param Username $username
 * @param Password $password
 * @return void
 */
private function __construct(UserId $userId, Email $email, Username $username, Password $password)
{
    $this->setId($userId);
    $this->setEmail($email);
    $this->setUsername($username);
    $this->setPassword($password);
}

There’s two things to notice here.

Firstly I’ve set the method to be private. This means we can’t use the following code to create a new user:

$user = new User();

Instead we will need to use a named constructor.

Secondly you will notice I’m using the class’ setter methods. This can seem a bit redundant, but I think it is a good practice to use setter methods even inside the class.

Next I will define the static register() method that will be used to register new users:

/**
 * Register a new User
 *
 * @param UserId $userId
 * @param Email $email
 * @param Username $username
 * @param Password $password
 * @return User
 */
public static function register(UserId $userId, Email $email, Username $username, Password $password)
{
    return new User($userId, $email, $username, $password);
}

And finally I will define the getters and setters for the class. You will notice that all of the setter methods are set to private. I want to be able to control the interface of the User entity and explicitly define how it should be used:

/**
 * Get the User's id
 *
 * @return UserId;
 */
public function id()
{
    return UserId::fromString($this->id);
}

/**
 * Set the User's id
 *
 * @param UserId $userId
 * @return void
 */
private function setId(UserId $userId)
{
    $this->id = $userId;
}

/**
 * Get the User's email address
 *
 * @return string
 */
public function email()
{
    return $this->email;
}

/**
 * Set the User's email address
 *
 * @param Email $email
 * @return void
 */
private function setEmail(Email $email)
{
    $this->email = $email;
}

/**
 * Get the User's username
 *
 * @return string
 */
public function username()
{
    return $this->username;
}

/**
 * Set the User's username
 *
 * @param Username
 * @return void
 */
private function setUsername(Username $username)
{
    $this->username = $username;
}

/**
 * Set the User's password
 *
 * @param HashedPassword
 * @return void
 */
private function setPassword(HashedPassword $password)
{
    $this->password = $password;
}

If you now run your tests, they should all pass green.

Conclusion

In today’s tutorial we’ve looked at a couple of important concepts.

Firstly we looked at how we can enforce the business rules of the application using native PHP code. By requiring certain arguments through the __construct() method, we can ensure that User objects will always satisfy the requirements of the business.

Secondly we look at how we can use the Ubiquitous Language to make our code more explicit. It’s important to write code in a way that makes sense to the business. If you are taking the requirements from your client and then translating them into a form that makes it easy to write your code, you are probably doing something wrong.

Finally we looked at creating a UserId class that takes control of the responsibility for generating entity ids. In the majority of web applications the entity id is generated from the database. However because I don’t want the database to leak into the domain of my application, I’ve decided to reverse the responsibility. This also gives us the opportunity to type hint for the UserId class in other parts of the application.

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.