I started playing with DDD recently. Today I'm having a problem with placing validation logic in my application. I'm not sure what layer should I pick up. I searched over the internet and can't find an unified solution that solves my problem.
Let's consider the following example. User entity is represented by ValueObjects such as id (UUID), age and e-mail address.
final class User
* @var \UserId
* @var \DateTimeImmutable
* @var \EmailAddress
* User constructor.
* @param UserId $userId
* @param DateTimeImmutable $dateOfBirth
* @param EmailAddress $emailAddress
public function __construct(UserId $userId, DateTimeImmutable $dateOfBirth, EmailAddress $emailAddress)
$this->userId = $userId;
$this->dateOfBirth = $dateOfBirth;
$this->emailAddress = $emailAddress;
I will try to answer your questions one by one and additionally give my two cents here and there and how I would solve the problems.
Non business logic related validation is performed by ValueObjects
Actually ValueObjects represent concepts from your business domain, so these validations are actually business logic validations too.
Entity - check it while creating User entity, in the constructor?
Yes, in my opinion you should try to add this kind of behavior as deep down in the Aggregates as you can. If you put it into Commands or Command Handlers you loose cohesiveness and business logic is leaking out into the Application layer. And I would even go further. Ask yourself the question if there are hidden concepts within your model that are not made explicit. In your case that is an
AdultUser and an
UnderagedUser (they could both implement a
UserInterface) that actually have different behavior. In these cases I always strive for modelling this explicitly.
Like email uniqueness. I read about the Specification pattern. Is it ok, if I use it directly in Command Handler?
The Specification pattern is nice if you want to be able to combine complex queries with logical operators (especially for the Read Model). In your case I think this is an overkill. Adding a simple
containsUserForEmail($emailValueObject) method into the
UserRepositoryInterface and call this from the Use Case is fine.
<?php $userRepository ->containsUserForEmail($emailValueObject) ->hasOrThrow(new EmailIsAlreadyRegistered($emailValueObject));
How to integrate it with UI validation?
So first of all there already should be client side validation for the fields in question. Make it easy to use your system in the right way and hard to use it in the wrong way.
Of course there still needs to be server side validation. We currently use the schema validation approach where we have a central schema registry from which we fetch a schema for a given payload and then can validate JSON payloads against that JSON Schema. If it fails we return a serialized
ValidationErrors object. We also tell the client via the
Content-Type: application/json; profile=https://some.schema.url/v1/user# header how it can build a valid payload.