Attribute Validation

Some people understand validation as the process whereby a service validates the state of a given object. In this case, the validation conforms to a Design-by-contract approach, which consists of preconditions, postconditions, and invariants. One such way to protect a single attribute is by using Chapter 3Value Objects. In order to make our design more flexible for change, we focus only on asserting Domain preconditions that must be met. Here, we'll be using guards as an easy way of validating the preconditions:

class Username
{
const MIN_LENGTH = 5;
const MAX_LENGTH = 10;
const FORMAT = '/^[a-zA-Z0-9_]+$/';

private $username;

public function __construct($username)
{
$this->setUsername($username);
}

private setUsername($username)
{
$this->assertNotEmpty($username);
$this->assertNotTooShort($username);
$this->assertNotTooLong($username);
$this->assertValidFormat($username);
$this->username = $username;
}

private function assertNotEmpty($username)
{
if (empty($username)) {
throw new InvalidArgumentException('Empty username');
}
}

private function assertNotTooShort($username)
{
if (strlen($username) < self::MIN_LENGTH) {
throw new InvalidArgumentException(sprintf(
'Username must be %d characters or more',
self::MIN_LENGTH
));
}
}

private function assertNotTooLong($username)
{
if (strlen( $username) > self::MAX_LENGTH) {
throw new InvalidArgumentException(sprintf(
'Username must be %d characters or less',
self::MAX_LENGTH
));
}
}

private function assertValidFormat($username)
{
if (preg_match(self:: FORMAT, $username) !== 1) {
throw new InvalidArgumentException(
'Invalid username format'
);
}
}
}

As you can see in the example above, there are four preconditions that must be satisfied in order to build a Username Value Object. It:

  • Must not be empty
  • Must be at least 5 characters
  • Must be less than 10 characters
  • Must follow a format of alphanumeric characters or underscores

If all the preconditions are met, the attribute will be set and the object will be successfully built. Otherwise, an InvalidArgumentException will be raised, execution will be halted, and the client will be shown an error.

Some developers may consider this kind of validation defensive programming. However, we're not checking that the input is a string or that nulls are not permitted. We can't avoid people using our code incorrectly, but we can control the correctness of our Domain state. As seen in the Chapter 3Value Objects, validation can help us with security too.

Defensive programming isn't a bad thing. In general, it makes sense when developing components or libraries that are going to be used as a third party in other projects. However, when developing your own Bounded Context, those extra paranoid checks (nulls, basic types, type hinting, and so  on.) can be avoided to increase development speed by relying on the coverage of your unit test suite.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset