Aggregates and domain validation
I was asked about using NHibernate in a Domain Driven scenario, with the Entities, Aggregates and Repositories. Specifically, with regard to doing validation. A domain driven problem needs a domain, so I'll chose the Northwind one. We have an Order aggregate, which has OrderLines, Shipments (which has OrderLines as well) and billing.
I am not so hot about validation (beyond rudimetry input validation), because I believe that Invalid State make the application flexible. Here is a simple business rule:
Which can be translated in the code to:
public void AddLine(OrderLine lineItem)
{
if( this.TotalCost + lineItem.TotalCost > CreditService.GetLimit(this.Customer))
throw new ValidationException("Passed credit limit");
orderLines.Add(lineItem);
lineItem.Order = this;
}
This is all very well, until you realize that when the user is browsing the store, they are likely to place many more items on the order than they actually intend to buy. The code above basically forces the user to stop what they are doing and make a decision about what items they want the most. That is not a good idea, because the user flow is interrupted with completely arbitrary and needless error.
I am more in favor of validating specific rules when the state change require it. In this case, I would check this business logic when trying to move the order to processed state. Another thing is throwing an exception for validation error. This is counter productive, because you need to guard against those, and it makes it harder to deal with violations of more than a single business rule.
Using the Domain Driven approach, we would like to use the Agrregate as the place where to put validation and business rules for the entities contained in the aggregate.
Often, a case is made for validating the state when the entities are being persisted, this can be an issue if/when you have partially loaded object graphs that you need to handle, while stil keeping the logic in its rightful place. I can think of several solutions to this, which mostly involves NHibernate intecetors and some attributes. I am not going to show them, because I don't believe that this is the right approach.
Validation may be a cross cutting concern, but I don't like the idea of it being trasperant. There is also the issue of "what do you do if it is invalid?" From an interceptor, the only thing that you can do is to throw, which put the session (and the related object graph) into an invalid state, not something that you should be eager to do.
I thought about utilizing the respective repositories for this, but I don't see the point here. Repositories are for the getting data, I am usually not explicitly saving stuff unless I have just created it (Unit Of Work is cool!), so there isn't any point in putting it in something like the Save() method there.
Thinking about this, I come back to Udi's discussion about task oriented interface, this is a far better approach in my opinion, since it means that we no longer talk in abstract terms, but rather in clear, meaningful domain concepts. So instead of having a validator that would check the state of an order, and run the appropriate validation, I would have a method on the Order class that would tell it to change its state, and it would validate itself. This goes for deep object graphs as well, in my opinion. There aren't many actions on the aggregate boundary that it isn't the one to initiate.
In short, don't try to find a technical solution to this problem, this is possible, but I feel that it would result in a clearer design and implementation to make this far more explicit.
Comments
Change of state and bussines rule validation...we can encapsulate this into a workflow.
You really like this example? Assuming that your AddLine belongs to the Order domain, it's accessing a service (??!). In a true domain driven design you'd use a policy (or even a specification) to encapsulate and be more expressive about this rule.
Also, in the light of ubiquitous language, there's a distinction between validation and invariants. You're enforcing invariants.
As I said, I don't like this approach, mainly because I gave an example where this is something that I want to break, so it is not an invariant, only validation for moving the order to processed state.
I can't claim that I am very good at DDD, the last system that I wrote which had an opportunity for a good domain model got gnarly in the UI, and my current projects doesn't have much of a domain model in them (not much point).
Nice stuff!
Am I following you if I say that you propose, in your northwind example, that the only way to change anything inside the Order aggregate would be to call something like...
myOrder.Modify(myChangeImpl);
no matter if the application runs in a SOA/distributed enviroment or not? I mean - you don't have any public setters at all inside the aggregate? None at all, whatsoever (as far as they can break a business rule)?
It's quite appealing in that sense, as you write, that business rules turns fully into a domain problem instead of a techinical problem. And the problem to do coarse grained optimistic locks I've fought with quite a lot recently turns a lot easier using this approach.
However - and I'm not sure I agree to myself here - won't it be quite messy after a while? In a deep graph where every entity belongs to the same aggregate, there will be numerous different ways to change this aggregate as I see it. Or is it maybe just because I haven't really used this approach before? Maybe I just don't see the consequences fully?
I think that quite often, very deep object graphs have aggregates trying to get out. In the example above, we have order that contains shipments, but we can also see a shipment as a standalone aggregate.
Comment preview