3

In a DDD designed application with CQRS principle how do we handle the case when a command needs to be validated before it is executed on an aggregate and the validation needs to query the existing data?

For instance, in my system I have Customer, Project and Ticket aggregates. A Project belongs to a specific Customer and a Ticket is opened for a specific Project.

A CreateTicket command is issued by the current customer. The system populates the command's payload with the customer_id and project_id.

Before the commands is executed on the Ticket aggregate, I need to validate that the command's project indeed belongs to the command's customer.

Another case is this. A command to create a new Department in the system is issued. A Department has a title property. Before the department is created I need to verify that there is no other department with the same title in the system. The aggregate repository does not query data. It only fetches by id, and stores aggregates.

How do we handle such cases?

3 Answers 3

2

In a DDD designed application with CQRS principle how do we handle the case when a command needs to be validated before it is executed on an aggregate and the validation needs to query the existing data?

If you need to query existing data that is already part of the aggregate, don't. Just pass the command to the aggregate, and put the logic for evaluating the current data into your domain code.

If you need to query data that exists somewhere else, then you normally will pass to the domain model something like a domain service, which gives the model the ability to access a copy of the data that it needs.

A command to create a new Department in the system is issued. A Department has a title property. Before the department is created I need to verify that there is no other department with the same title in the system.

The general term for this problem is Set Validation. Priority one is to make sure that you understand the business impact (how much does it cost the business to have a duplicate department title in your database for a minute?)

Domain models enforce set invariants by including the entire set in a single "aggregate", so that membership changes always have the data that they need to check them. In systems where the set members are being stored in a relational database, it may make sense to have the database, rather than the domain model, enforce the invariant.

When there is only a single field that you need to worry about being unique, and if for that entity the value of the field will never change, a possibility is to use that field to compute the identifier (ensuring that all requests for a specific unique key get mapped to the same instance of the aggregate).

(That's probably not a good option for something like a department name, because re-orgs are so foreseeable).

1

Aggregates should be designed in a way that they always have all the data they need to execute the business logic they encapsulate. Therefore, if you find that you need to query data from other areas (specially from other Bounded Contexts), it might indicate that your aggregate boundaries are wrong or, at least, that you are not storing enough data in your aggregate.

In your particular scenario, why don't you store CustomerId and ProjectId when you create the Ticket? if your create operation is safe (don't allow customers to create tickets for projects that they don't own), then from that point on you can execute commands on tickets knowing that CustomerId can indeed access that ProjectId.

Regarding the validation of Department name, the easiest and safest option to me is to have a unique constraint in the database (if the db technology allows it), because no matter how many checks you add before saving, there is always a chance of a concurrent insert or update and end up with a duplicate department name.

To improve the user experience, you can expose and endpoint to validate a department name, so that the UI can do an initial validation and avoid most cases from the UI directly. Then the unique constraint is there to save you from concurrency issues.

0

If you need to query something in somewhere else out of aggregate boundary, validate the data before dispatching the command. In most cases, the command bus mechanism is 'Fire and forget' and if you need to return some error to the command issuer, you should check that constraint before dispatching commands.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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