6

I asked this question a while ago: What is the "best" way to apprach validation from the perspective of a DDD purist?

At the time I decided to put the validation logic inside the domain object constructor like one of the answerers suggested.

The business have recently become interested in how data has arrived at its current state. Therefore I am familiarising myself with Event Sourcing and also CQRS. I am unsure where to put the validation logic when using CQRS. For example, please see the Command object below:

public class RequestBookingCommand : Command
    {
        public RequestBookingCommand(int courtId, int hour, int length, string userName, string notes)
        {
            Name = "AddBooking";
            CourtId = courtId;
            Hour = hour;
            Length = length;
            UserName = userName;
            Notes = notes;
        }

        public int CourtId { get; private set; }
        public int Hour { get; private set; }
        public int Length { get; private set; }
        public string UserName { get; private set; }
        public string Notes { get; private set; }
    }

There is also a domain object that accepts the RequestBookingCommand in the constructor like this:

public RequestBooking(RequestBookingCommand requestBookingCommand)
        {
            Name = requestBookingCommand.Name;
            CourtId = requestBookingCommand.CourId;
            Hour = requestBookingCommand.Hour;
            Length = requestBookingCommand.Length;
            UserName = requestBookingCommand.UserName;
            Notes = requestBookingCommand.Notes;
        }

I believe I have three options for the validation:

Option 1 - Put the validation in the Command object only.
Option 2 - put the validation in the Domain object only.
Option 3 - Put the validation in both the Command object and the domain object

An example of validation is (it is simple):

if (CourtId = 0)
{
    throw new ArguementException();
}
//Ensure the rest of the members are populated with values.....

At first I thought option 2 or 3 was the most appropriate because the Domain object should be responsible for maintaining the invariants and also the business analysts have requested this validation. However everywhere I look suggests that the validation logic should go in the Command object e.g. here and here. Steven also suggests it here.

It appears to me that putting the validation logic in a command is like putting it in the application service (making the domain model anemic). Is that not the case?

I realise my application will work regardless of what option I choose. However, I am trying to follow the principle of least astonishment here.

Update

Following on from VoiceOfUnreasons answer. Say I have a class called Customers and also a class called CustomerCommand:

public class CustomerCommand
{
  public Guid id;
  public string Age;

  public CustomerCommand()
  {
       If (Age <0)
       {
            throw new ArgumentException();
       }
       If (Id==Guid.Empty())
       {
            throw new ArgumentException();
       }
  }

}

public class Customer 
{
  public Guid id;
  public decimal Age;
  public IOffer Offer;

  public Customer()
  {
       If (Age < 18)
       {
            throw new ArgumentException();
       }
  }

  public AssignOffer(List<IOffer> allOffers, IOfferCalculator offerCalculator)
  {
     Offer = offerCalculator.calculateOffer(this, allOffers);
  }

}

Say the company only creates an offer for customers over the age of 18. In this example code I believe:

1) The command is validating that it is well formed e.g. a person cannot be aged -50 because this is impossible.

2) Given the customer is over 18, then assign an offer.

3) If the customer is over 18, then the domain object can satisfy all queries.

Does this satisfy your three parts?

  • Maybe you need a class ValidatedBookingRequest which is contained by both the command and the domain object. That would ensure validation occurred everywhere it needed to be, using a "trusted" third party, namely the ValidatedBookingRequest class. – BobDalgleish Jun 5 '18 at 14:13
  • 1
    Your example is not a good one - a RequestBookingCommand holding exactly the same attributes as a RequestBooking object would probably utilize an object the of the latter type, which means validation implemented in the RequestBooking constructor would automatically be reused in the RequestBookingCommand object. – Doc Brown Jun 5 '18 at 21:29
  • @Doc Brown, are you saying that the Command would reference the Domain object i.e. there would be a member called: RequestBookingCommand.RequestBooking? – w0051977 Jun 8 '18 at 10:49
  • @Doc Brown, everywhere I read tells me the validation should be contained in the commands. My commands are stored in a separate project because some are used by multiple microservices. what would happen if another developer subclassed the command and did not bother adding any validation? I believe if this happened then the Domain Object could be in an invalid state. – w0051977 Jun 8 '18 at 10:50
3

One area that I found very confusing in the early going is that messages and domain values are not really the same thing.

In particular, messages are part of your API - they describe an agreed upon protocol/representation for communicating semantic information across the boundary of the domain model. In situations where different organizations are sharing messages (example, a client sending messages to a server), messages schemas tend to change slowly, and with a great deal of care for backwards compatibility.

Value objects are not part of your API; they are domain semantics applied to data structures. So you can change those as aggressively as you like - because ultimately, the domain objects don't leak out of the boundaries of your service.

Over the lifetime of a successful project, you are likely change your value objects much more often than you change your messages.

If you are familiar with Gary Bernhardt's talk Boundaries: messages are how we replicate information from one imperative shell to another. Our imperative shell turns the message into values which are used by the functional core.

Microservices don't share value objects. They share message schema - that allows either team to change up their domain model as much as they like without forcing cascading maintenance across the entire system.

That means we might be expecting the version 2.1 representation of a message, but we are expected to be able to handle 2.0 (backward compatibility) and 2.2 (forward compatibility).

Note: messages are not limited to a single representation either. You might have one channel listening for messages expressed as byte arrays that can be decoded using a JSON parser, and another channel listening for messages expressed as byte arrays that are application/x-www-form-urlencoded.

It's our adapter that is responsible for ensuring that the bytes we have received can be sensibly understood as a message of the appropriate schema. Prior to that point, the data is untrusted -- so you certainly want to have validation in place as you take the domain agnostic bytes and create an in memory representation of the message semantics.

Converting the message to a value object - you are really going from one trusted representation to another. There's a lot less risk there; faults that are introduced at this point are much more likely to be programmer error than they are malicious attacks.

But there are likely to be cases where you aren't working with your adapter. For instance, the tests that you run between changes to ensure you have broken anything -- do you need validation there?

Furthermore, the concept of "untrusted" data gets fuzzier when dealing with stand alone applications. For example; does your desktop calculator really need separate message and value object models?

Domain Modeling Made Functional has a lot of good material on this topic; but in short you can treat validation as a pipeline, and make "this data is untrusted" an explicit concept in your program.

But while "trust" is an important element of some domains, the dangers of malicious byte representations of messages are part of the "secure software domain", and should be modeled there - not in the core business logic (as a rule).

To the best of my knowledge, this isn't a topic well covered in the Blue Book -- I simply can't remember any details discussions of the problems of distributed messaging.

Are you saying that if I remove primitive obsession, then all of the validation is done in the domain? Is there any validation done on the command?

No. I'm saying that you have two separate logical translations

Untrusted Message Representation -> Trusted Message
Trusted Message -> Domain Values

To offer a contrived example: your message specification may say that Estimated Arrival Time is an ISO-8601 Local Date, where your domain model tracks that information as a System.DateTime.

Untrusted Representation -> ISO Local Date
ISO Local Date -> System.DateTime

Later on in the project, you realize that tracking Estimated Arrival Time as "seconds since epoch" meets your needs better, you change the value objects in your domain to use the new in memory representation.

Untrusted Representation -> ISO Local Date
ISO Local Date -> long

And so your Value Object changes, and the adapter that translates from Trusted Message to Value Object changes.

But the message schema that you use to communicate with other processes stays the same.

The motivation for "validation" in the two cases is very different. In the first, we are using run time checks to protect the system from corrupt data, malicious data, and messaging errors by other parties (our partner sends a message to the wrong endpoint, a message gets misdirected by the routing middleware).

In the second, we're guarding against errors being introduced by the impedance mismatch between our domain semantics and the domain agnostic in memory representations that we use.

So, in short: ideally, you should have "validation" in both your messages and in your value objects. But the trade offs in the two cases are not the same, and there are circumstances where you might reasonably choose one without the other.

Every single example I look at has only context validation.

Not a fan - I'm more comfortable with a different type in each context:

  • I prefer having the differences in semantics between validated and unvalidated representations be explicit in code
  • I often program in languages with type checking; using explicit types catches some of the errors I make

I don't mind re-using the same underlying data structure at different stages -- the expression of the semantics in memory is an implementation detail.

Note that to some degree, it's a quibble between

class UntrustedMessage {
    boolean isValid();
}

which I hate, and the almost equivalent

class UntrustedMessage {
    Optional<TrustedMessage> validate();
}

As a thumb rule: you use validation (in the command) to ensure the input data is in valid format, then the business rules (in the domain) decide how/if the input changes a model

This isn't quite right. I see three parts, rather than two

  • Is the "command" message well formed? This is basically a validation check against the messaging schema. Is Withdraw(200 USD) a correct message?

  • Given the current state of the domain, what should we do with this message? That's business rule, it's the semantics of what's supposed to happen. I think of this as part of the problem domain. Somebody withdrew 200USD - do they have funds to cover that? do we need to escalate? notify government authorities? offer them overdraft protection? All of this is variations on "figure out how this message changes the current domain state".

But the part that's missing is one I'm trying to demonstrate is separate from message validation

  • Are the domain objects in a valid state? With is to say, are they in a condition that allows them to satisfy the post conditions of all queries that might be asked of them.

Crazy thought experiment - suppose our domain model had SmallMoney and BigMoney; we've discovered in our experience of the domain that there are different processes in place for small amounts of money vs large amounts -- you can get SmallMoney from an ATM, or a cashier, but BigMoney requires talking to a bank officer, or whatever. So SmallMoney, the value object, has a post condition like SmallMoney::toInt returns a number in the range 0-100.

That has nothing to do with the message schema at all, and it is only loosely coupled to the domain logic (which is to say, we're pretending that SmallMoney means something to the domain experts), but we use validation on the object so that we can do it in one place, rather than everywhere/not at all.

  • Thanks for the first hyperlink. +1. Are you saying that if I remove primitive obsession, then all of the validation is done in the domain? Is there any validation done on the command? – w0051977 Jun 6 '18 at 10:59
  • Could you specify, which option you are recommending before I accept. Thanks. – w0051977 Jun 6 '18 at 12:01
  • Thanks for the update. In that case the validation goes in both places i.e. the command object and the value object? – w0051977 Jun 6 '18 at 12:43
  • Finally, would you duplicate the validation code or use a decorator? I don't like the idea of Fluent Validation because of domain leakage. – w0051977 Jun 6 '18 at 12:50
  • I would recommend that you start by duplicating, and judge how painful that is in practice. I believe that coupling the two together can work, but the details matter, and that path leads into the fog of "it depends". – VoiceOfUnreason Jun 6 '18 at 13:57
9

Where you put your validation largely depends on what kind of validation in which you are engaging for a particular object. What I mean by that is that not all kinds of validation need to be in your domain. Superficial validation, that is validation that requires absolutely no other piece of state, is suitable in your application layer. Where as domain validation, that is validation more focused on the business rules, should be in your domain. A reasonable line from which to draw this distinction can be based on whether it's possible for the command to validate itself given no other bits of data (or maybe one other single piece of context that is consistently applied to all commands).

For example, checking whether a value exists or that a date string has the proper format needn't be pushed down into your domain. On the other hand, checking if a Buyer has good enough standing to place an Order needs to be in your domain. Not only would this likely need more data than is available on the PlaceOrderCommand itself, this particular invariant is a business rule that applies as part of a business process in a way that checking the length of some input string is not.

Whether the validation is on the Command itself or extracted into a specific Validator-type object is up to you. It's largely just semantics. Because you seem to prefer having rules in your domain, why not find a conceptual middle ground and create a distinction between commands that have not yet been validated and those that have? Where the ValidatedCommand is the only type know by your domain. This may provide a conceptual framework you find suitable going forward that helps bridge the gap in a structured way.

Now, this can be taken too far. Although the kind of validation you are doing is a good indicator of where to put the validation logic, there are some pieces of data requiring superficial kinds of validation that are, indeed, part of your domain. An email address is a good example of this. Although you may be able to simply validate strings to make sure they are formatted correctly as an email address, a person's email is likely an important piece of business data talked about and used in all sorts of places and ways. Because of this, it should likely be encapsulated (and validated) in an EmailAddress domain object.

Given a certain amount of knowledge crunching, you may well find a whole host of Value Objects that have found their way into your ubiquitous language that can then be used as a place for validation instead of in your commands. In this way you can not only reduce coupling and duplication, but also mitigate the amount of validation happening outside your domain.

For your specific question, maybe CourtId, Hour, Length, and Notes are really just a decomposed CourtReservation object? Always be on the lookout for natural groupings of data and give them names!

Edit: Reading this back, a more in-depth discussion regarding the practical considerations of "superficial validation" is warranted. Typically, superficial validation is analogous to the validation being done in your persistence store (think RDBMS here). As a result, omitting superficial validation in your commands should likely (maybe not always) still raise an exception deeper in your system during persistence.

For example, if [UserName] is a VARCHAR(128) NOT NULL field in your database, I would expect there be some analogous validation occurring somewhere that mimics these requirements at a higher level. Nearly all field validation that can be done by a database table (save relationships) would qualify as superficial validation. This not only includes data bounds (string and number limits), but also potentially things like "[StartDate] must be less than [EndDate]" - as this could also be enforced with field constraints. Any validation that would require a trigger (shudder) is almost certainly better enforced by your domain. Again, the above is just a rule of thumb that may serve you well as these issues crop up.

This makes sense right? Why else would a string need to be less than 128 characters? By including this kind of superficial validation, you can catch errors much sooner and avoid expensive operations that are going to fail at the last possible step during commit.

  • If you put the superficial validation in the command, then are you not potentially allowing the domain model to be not always valid? e.g. if the developer makes a mistake in the command. – w0051977 Jun 7 '18 at 16:00
  • See my edit above. – king-side-slide Jun 7 '18 at 18:21
  • Thanks +1. Are you suggesting that value objects are created for each field and validation done in there? – w0051977 Jun 7 '18 at 18:52
  • Creating a value object for each field is almost certainly too granular a strategy to be maintainable. There will be certain fields that are themselves part of your ubiquitous language and therefore can be encapsulated. Examples may include data like UserName and Email. Data that both stand on their own and are used in many different places are good candidates. How many commands in your application contain one of: CourtId, Hour, Length, or Notes but not all of the others? CourtReservation may be a natural grouping of data and as a result a good boundary for validation as well. – king-side-slide Jun 7 '18 at 19:24
  • If the commands are stored in a separate project to the domain model then I guess the command should be validated inside the RequestBooking domain object constructor (like I have done in the question). Do you agree with that? – w0051977 Jun 7 '18 at 19:34

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.