2

I have a value object to hold a user id number as a string. This number has a unique format throughout my domain. So, it's being validated inside the object during instantiation and an exception is thrown if it's not of the correct format. The sample class is given below (all codes are in PHP):

class IdentityNumber
{
    public function __construct(string $id)
    {
        $this->validate($id);
        $this->id = $id;
    }

    private function validate(string $id): void
    {
        if (! preg_match('/^\d{2}-\d{5}$/', $id)) {
            throw new \Exception("Invalid identity number");
        }
    }
}

Up to this everything makes sense.

Now, the issue is: if I need to know whether a string is a valid identity number, I'll have do the following:

function isValidIdNumber(string $id): bool
{
    try {
        new IdentityNumber($id);
        return true;
    } catch (\Exception $e) {}

    return false;
}

Which is very ugly!!

So, I suggested writing the validator as a static function, and changing the class to this:

class IdentityNumber
{
    public function __construct(string $id)
    {
        if (! static::isValid($id)) {
            throw new \Exception("Invalid identity number");
        }
        $this->id = $id;
    }

    public static function isValid(string $id): bool
    {
        return (bool) preg_match('/^\d{2}-\d{5}$/', $id);
    }
}

This way, I could check the validity of an id number, outside the class without instantiation, like:

IdentityNumber::isValid($id)

But a senior, who is much more familiar with the DDD concepts, said that this is bad practice since it makes the value object look like a service object. But I cannot see how this could be bad and now I'm confused.

Can someone tell me if there is something wrong in using static validator function in value objects that are certain to be the same format throughout the domain (strictly within DDD concepts)? If yes, what are the alternatives?

3
  • 1
    I don’t know PHP, but in C# I would give the value object a static factory method that returns a Result<T>. If the result is a failure, it contains the error, if it’s a succes, it has the instance of the value object.
    – Rik D
    Nov 10, 2021 at 20:42
  • 2
    Under what circumstances are you going to need to check whether a string is a valid identity? I'd suggest you just always use the constructor and then don't handle the exception. The exception probably indicates a bug somewhere allowing invalid IDs into the system so you should just let the application crash/propagate the exception to a higher level handler. Nov 16, 2021 at 11:34
  • I'm using it in the Laravel framework's validator for request inputs. So, a true/false response is expected. I think the problem here is the mixed coding methodologies, but it's unavoidable in our case. Nov 17, 2021 at 15:23

1 Answer 1

1
  • Is something wrong to use static validator function in value objects?

I see the point of your senior colleague. Arguable, it may violate the single responsibility principle. Using a Value object to validate external values may not be the perfect design. But I understand the forces that lead you to this design that is not necessarily bad

  • What are the alternatives?

I would create a separated IdentityNumberValidator with the static function that IdentityNumber will call from its constructor. This will separate the responsibilities but the price to pay is less cohesion. The logic of IdentityNumber and IdentityNumberValidator is separated in different classes. You could say that IdentityNumberValidator is a small DDD service but in some situations may be an overkill.

1
  • There doesn't seem to be any completely right answer here :D So, going with the ugly code I mentioned in the question and maintain the validation within the value object (since it'll need minimum code changes in my case). Another time, I'd chose your alternative... Nov 20, 2021 at 12:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.