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.
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.