Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use reusable validation in a ValueObject

I'm trying to get my head around combining some techniques.

It seems good practice to never make it possible to create a ValueObject that is not valid. The ValueObject constructor therefor should fail whenever the provided content is not good enough to create a valid ValueObject. In the examples I have, an EmailAddress object can only be created when there is a value present. So far, so good.

Validating the value of the provided emailaddress, that's where I begin to doubt the principles. I have four examples, but I can't tell which one should be considered the best practice.

Example 1 is the easy one: simply a construct function, a required parameter "value", and a separate function validate to keep the code clean. All the validation code stays inside the class, and will never be available to the outside world. The class has only one purpose: store the emailaddress, and make sure it will never be an invalid one. But the code will never be reusable - I create an object with it, but that's all.

public function __construct ($value)
{
    if ( $this->validate($value) )
    {
        throw new \ValidationException('This is not an emailaddress.');
    }
    $this->value = $value;
}

protected function validate ($value)
{
    return is_string($value); // Wrong function, just an example
}

Example 2 makes the validate function a static function. The function will never change the state of the class, so it is a correct use of the static keyword, and the code in it will never be able to change anything to any instance created from the class embedding the static function. But if I want to reuse the code, I can call the static function. Still, this feels dirty to me.

public function __construct ($value)
{
    if ( $self::validate($value) )
    {
        throw new \ValidationException('This is not an emailaddress.');
    }
    $this->value = $value;
}

public static function validate ($value)
{
    return is_string($value); // Wrong function, just an example
}

Example 3 introduces another class, hardcoded inside the body of my object. The other class is a validation class, containing the validation code, and creates thus a class that can be used whenever and wherever I need a validation class. The class itself is hardcoded, which also means that I create a dependency on that validation class, which should be always nearby, and is not injected through dependency injection. One could say that having a validator hard coded is as bad as having the complete code embedded in the object, but on the other hand: DI is important, and this way one has to create a new class (extending, or simply rewriting) to simply change the dependency.

public function __construct ($value)
{
    if ( $this->validate($value) )
    {
        throw new \ValidationException('This is not an emailaddress.');
    }
    $this->value = $value;
}

protected function validate ($value)
{
    $validator = new \Validator();
    return $validator->validate($value);
}

Example 4 uses the validator class again, but puts it in the constructor. My ValueObject thus needs a validator class already present and created, before creating the class, but it is possible to easily overwrite the validator. But how good is it for a simple ValueObject class to have such a dependency in the constructor, as the only thing really important is the value, it should not be my concern to know how and where to handle if the email is correct, and providing a correct validator.

public function __construct ($value, \Validator $validator)
{
    if ( $validator->validate($value) )
    {
        throw new \ValidationException('This is not an emailaddress.');
    }
    $this->value = $value;
}

The last example I started thinking about, is providing a default validator, and meanwhile make it possible to inject through DI an overwrite for the validator in the constructor. But I started doubting how good a simple ValueObject is when you overwrite the most important part: the validation.

So, anyone has an answer which way one should best write this class, that is correct for something as easy as an emailaddress, or something more complex like a barcode or a visa card or whatever one may think about, and doesn't violate DDD, DI, OOP, DRY, wrong use of static, and so on...

The complete code:

class EmailAddress implements \ValueObject
{

protected $value = null;

// --- --- --- Example 1

public function __construct ($value)
{
    if ( $this->validate($value) )
    {
        throw new \ValidationException('This is not an emailaddress.');
    }
    $this->value = $value;
}

protected function validate ($value)
{
    return is_string($value); // Wrong function, just an example
}

// --- --- --- Example 2

public function __construct ($value)
{
    if ( $self::validate($value) )
    {
        throw new \ValidationException('This is not an emailaddress.');
    }
    $this->value = $value;
}

public static function validate ($value)
{
    return is_string($value); // Wrong function, just an example
}

// --- --- --- Example 3

public function __construct ($value)
{
    if ( $this->validate($value) )
    {
        throw new \ValidationException('This is not an emailaddress.');
    }
    $this->value = $value;
}

protected function validate ($value)
{
    $validator = new \Validator();
    return $validator->validate($value);
}

// --- --- --- Example 4

public function __construct ($value, \Validator $validator)
{
    if ( $validator->validate($value) )
    {
        throw new \ValidationException('This is not an emailaddress.');
    }
    $this->value = $value;
}

}
like image 768
MetalArend Avatar asked Jan 21 '14 20:01

MetalArend


People also ask

Can a value object reference an entity?

A value object can reference other entities. For example, in an application that generates a route that describes how to get from one point to another, that route would be a value object.

Which is an example of value basic object?

In computer science, a value object is a small object that represents a simple entity whose equality is not based on identity: i.e. two value objects are equal when they have the same value, not necessarily being the same object. Examples of value objects are objects representing an amount of money or a date range.

What is the difference between entity and value object?

A Value Object is an immutable type that is distinguishable only by the state of its properties. That is, unlike an Entity, which has a unique identifier and remains distinct even if its properties are otherwise identical, two Value Objects with the exact same properties can be considered equal.


2 Answers

Example 4!

Why? Because it's testable, plain and simple.

Depending on what your validator actually does (in some circumstances your validator may rely on an API call or a call to a database) the injectable validator is completely testable via mocks. All of the other's are either impossible to test under the circumstances I just mentioned, or incredibly hard to test.

EDIT: For those wondering how the dependency injection method helps with testing then consider the CommentValidator class below that utilises a standard Akismet spam checking library.

class CommentValidator {
    public function checkLength($text) {
        // check for text greater than 140 chars
        return (isset($text{140})) ? false : true;
    }

    public function checkSpam($author, $email, $text, $link) {
        // Load array with comment data.
        $comment = array(
                        'author' => $author,
                        'email' => $email,
                        'website' => 'http://www.example.com/',
                        'body' => $text,
                        'permalink' => $link
                );

        // Instantiate an instance of the class.
        $akismet = new Akismet('http://www.your-domain.com/', 'API_KEY', $comment);

        // Test for errors.
        if($akismet->errorsExist()) { // Returns true if any errors exist.
            if($akismet->isError('AKISMET_INVALID_KEY')) {
                    return true;
            } elseif($akismet->isError('AKISMET_RESPONSE_FAILED')) {
                    return true;
            } elseif($akismet->isError('AKISMET_SERVER_NOT_FOUND')) {
                    return true;
            }
        } else {
            // No errors, check for spam.
            if ($akismet->isSpam()) {
                    return true;
            } else {
                    return false;
            }
        }
    }
}

And now below, when you're setting up your unit tests we have a CommentValidatorMock class that we use instead, we have setters to manually change the 2 output bools we can have, and we have the 2 functions from above mock'd up to output whatever we want without having to go through the Akismet API.

class CommentValidatorMock {
    public $lengthReturn = true;
    public $spamReturn = false;

    public function checkLength($text) {
        return $this->lengthReturn;
    }

    public function checkSpam($author, $email, $text, $link) {
        return $this->spamReturn;
    }

    public function setSpamReturn($val) {
        $this->spamReturn = $val;
    }

    public function setLengthReturn($val) {
        $this->lengthReturn = $val;
    }
}

If you're serious about unit testing then you need to use DI.

like image 59
Garry Welding Avatar answered Sep 28 '22 03:09

Garry Welding


The first instinct is usually the best. You should use the first option. EmailAddress is a value object. It can be reused in other value objects or entities. I don't understand why you think it's not reusable. You can have a "shared library" of these common value objects used in other bounded contexts. Just be careful what you put in there. They would need to be truly generic if that's even conceptually possible.

like image 22
JustinS Avatar answered Sep 28 '22 04:09

JustinS