Is this an anti pattern?
I see two anti-patterns here
- passing around references to objects before their initialization is complete is a bad idea.
- using an abstract validator to do the checking suggests that you aren't respecting the principal of cohesion.
More generally, I think there's a bit of confusion in Khorikov's essay: we don't validate entities, we validate state. The entity is just an identity progressing from one state to the next.
![enter image description here](https://i.stack.imgur.com/5dgoG.jpg)
For more on this point, see Stuart Halloway's 2011 talk.
The state that you are validating is a value object, in Evans's language.
Throwing an ArgumentException in the constructor when the state you've been given doesn't satisfy your invariant is a perfectly reasonable thing to do.
But I still wouldn't inject a validator to do the state checking -- that sounds as though we are treating the entity as a dumb bag of state, with all of the state living elsewhere. Compare with Martin Fowler's discussion of anemic domain models, and you'll see what I mean.
Would you suggest something like: Execute / CanExecute pattern for the validation?
I'm not a fan; but the reasoning is a bit complicated.
public IReadOnlyList<string> CanDeliver(string address, DateTime time)
So the first thing to notice is that this is a query, which means it should be safe; it doesn't induce a mutation in the entity.
What distinguishes this query from being a full function is the current state of the entity. That is to say, CanDeliver is partial application, with an implicit variable bound to the current (immutable) state. So let's make that binding explicit:
public IReadOnlyList<string> CanDeliver(Order.State currentState, string address, DateTime time)
Notice that the entity now goes away -- that is, the law of Demeter is telling us that CanDeliver
belongs on the state, not on the entity.
If that's the case, then you can call State.CanDeliver any time you like, in the controller, or even in the client when composing the request. But as David Arno points out, you still have the race, which is why Khorikov still needs the check.
I have also injected RulesOfferCalculator into this class. What do you think of that?
Injecting domain behaviors into an entity is weird, and my guess probably wrong in most cases.
Review the definition of SERVICE in Domain Driven Design
- The operation relates to a domain concept that is not a natural part of an ENTITY or VALUE OBJECT
- The interface is defined in terms of other elements of the domain model
- The operation is stateless
In common cases, a domain service is passed to an entity as an argument, rather than embedded within the entity as though it were a piece of state.
From what I gather, his point is that domain objects will not generally map 1-to-1 to the DTOs, in the sense that they will not have all the same properties. In fact, domain objects may have very few getters and setters; they may even have none at all. This is related to the the "tell, don't ask" principle: if responsibilities are properly distributed across classes, domain objects will have very little need to pull data from each other in order to achieve something. Instead, they will mostly call methods on each other. Now, while you can use Automapper-like libraries to copy data from DTOs to the domain objects in such a scenario, the author's point is that this requires a fairly complicated setup, which defeats the purpose of having an automapper.
They can perform the mapping of course, but that would require setting up quite sophisticated mapping rules which diminishes the value of having the automapper in the first place.
If the design of your domain objects is influenced by the desire to support easy automapping, then you'll break encapsulation by introducing getters and setters that are not really needed. This could introduce problems with validation. E.g., without automapping considerations, your domain objects may be designed in such a way so that it is not possible to invoke a method or a property and have the object end up in an invalid state when the control returns to the caller. If you introduce getters and setters and a Validate() method, then it becomes possible for an object to be in an invalid state between the a call to a setter, and a call to Validate(). That may or may not be OK - as everything else, it's a trade-off.
Best Answer
It's an interesting question that seems to come up in a variety of guises.
I am of the opinion that the best approach is to allow the concept of an object that is a invalid state.
The reason being that the validation rules for an object are usually not set in stone. They may change over time, or be different for different operations. Thus an Object which you created, populated and persisted some time ago, may now be deemed invalid.
If you have setter or constructor validation checks, then you have a big problem in that your application will error when you try to retrieve these entities from your database, or reprocess old inputs etc.
Additionally I don't think that business rules incorporate simple yes/no validation for the most part. If your domain is selling cakes and you don't deliver south of the river, but someone offers you a million pounds to do it. Then you make a special exception.
If you are processing millions of applications and you have strict rules about what characters can be in a field, then you probably have a process for correcting bad fields. You don't want to be unable to accept a bad field at all, it just follows a different path through the Domain.
So, if in the code you are so strict that 'invalid' data can just never exist because the constructor would throw an exception, you are bound to be brittle and fail for questions like "how many people filled the form in wrong?"
Allow the data and fail the operation. This way you can adjust the data or rules for the operation and re run it.
example:
So here we are unable or don't wish to deliver to blank addresses. The Domain object Order, allows you to populate a blank address but will throw a exception if you attempt to deliver that order.
An application, say a queue worker processing orders from json data stored in a queue, coming across an 'invalid' order:
Is able to create the Order object, as there are no validation checks in the constructor, or the setter for Address. However, when the Deliver method is called it will throw the exception and the application will be able to take an action. eg
The Application can still work with the Invalid Order, moving it to the error queue, accessing its Id, reporting on errors etc. But the Deliver Operation contains the Domain logic of how you want to handle Delivery to blank addresses.
If the Domain logic later changes with further requirements:
Then you can still process orders on the queue which were generated when UK addresses were allowed and get the expected result.
Its interesting to compare my answer with the one from @VoiceOfUnReason.
I've used a string Address for simplicity and also because there is no absolute definition of what an address is. But if we have a more absolute definition, say his Deposit always has a currency. It's nonsensical to even talk about a deposit with no currency.
In that case yes, you can define a new value type which simply cant exist unless you have specified the currency and loads of potential errors in your code will simply not be possible.
But you have to be sure its a fundamental thing. Otherwise you are asking for trouble later!