CQRS – Where to Put Format Validation in a Domain Model

cqrsdomain-driven-designvalidation

It feels right to put format validation inside the domain objects (VOs or entities) because it is the natural place for high cohesion and the domain knows best what every domain description/attribute/property means. Many DDD practitioners and books authors (Vaugh Vernon, Dan Bergh – and even Eric Evans suggest , but on a different aspect: authorization, to model the domain to reflect these matters) suggest to design the domain model in a way to reflect and enforce a proper state of the business.

I agree that format validation (ie.: address property in EmailAddress VO should be max ~250 chars and should match a regex) should be implemented inside the domain objects. But these validation checks work best with a domain model that will be used for both state changes and state queries.

What about CQRS where the command side (where the domain model is) has little or no knowledge about the read side. Should one reimplement the same input format checks in the query side of the application? In CQRS there are some common responsibilities for both command and query side (like input format validation), so the usual format validation implementations that most of DDD practitioners suggest will be duplicated.

How should one deal with this, w/o making the domain model (from the command side) to be anemic and w/o making unaware of the state of the attributes it will hold .

An example : there might be a feature that would let a conference owner to schedule a conference and change the number of available seats. But because the conference room is not big enough, the maximum allowed number of seats would be 100. So a business rule would be that : a conference owner cannot add more than 100 seats for a scheduled conference (also minimum 1 seat needed for the conference to be in a valid state). So a method would be on a ConferenceAR :

changeNumberOfAvailableSeats(numberOfAvailableSeats) {
    if(!isNumber(numberOfAvailableSeats) || numberOfAvailableSeats > 100 ||   
            numberOfAvailableSeats < 1) {
        throw new DomainException ...
    }  

    // Change the number of available seats ...
}

On the query side there might be an UI page where you can find all the scheduled conferences that have a certain number of available seats. So again , on the server's query side of the application, there must be a format validation that will check if the query for scheduled conferences with a number of seats available is a number and is in the range 1-100. This query might be needed by someone who wants to reserve a certain number of seats for a conference . Also conferences might be on the same topic so one could see a conference with the same topic many times and choose the one cheaper or with more seats available.

So again, should one reimplement the format validation on both query and command sides or there is another solution?

P.S. : there are many format validation duplicates (on both command and query side) like the email format validation , or a currency format validation (if the currency is : USD || CAD || AUD etc …)

P.P.S. : Another question that popped up : Does the query side of the application require any input format validation ? If the input is not a valid format it will return nothing , query found no data related to the request. I see that format validation on the query side is purely a security measure (ie. for buffer overflows). So is the input format validation really required on the query side ?

Best Answer

I would think if your domain objects on the command side are always in a valid state, you shouldn't need to worry that the queries return invalid results.

What would you do, anyway, if you discovered an invalidity on the read-side, anyway? Tell the read repository to re-enter the values? ;-)

If the read side were to have checking, it would think it would be in the area of some kind of unit or integration testing, because instead of a data entry error message, it's a bug.

One of the things about CQRS is prying apart those concerns, plus just having stuff in one place. Otherwise it makes it harder, not easier (really, it can be as easy or easier, otherwise, look for a better way to implement it or keep studying it).

Related Topic