Posts
1150
Comments
891
Trackbacks
1
DDD-Lite: Mama don’t let your entities grow up to be invalid

Update: Troy pointed out that using Address as an example wasn't good, because in DDD, Address is considered what is called a 'value object' which has different features than what are called 'entities.'  Since the point I was trying to make was about validation and why eliminating setters is an important concept, that wasn't totally relevant, but it's a valid objection (pun intended) as it could be misleading.  So, I'm going to update the example.  In a minute or two.  Just keep that in mind till then.

One of the hardest things about ‘continuous improvement’ (which I think really amounts to ‘not sucking more at the end of the day’, but I digress….seriously, some steps one might take to become a better developer are steps backwards, which I think the ‘craftsmen’-type people don’t discuss enough…but I digress) is trying to keep up with all of the possible information out there, from blogs, whitepapers, and other things.  I estimate the number of posts I get in my various readers are dozens a day.  Even filtering out the fluff, there is a lot of great stuff out there.  On top of that, I subscribe to a number of mailing lists, which just increases the number of things to read on a daily basis if I want to keep up.

Blah, blah, blah.  Anyway, two notions/concepts/thoughts pop up on the Yahoo DDD mailing list that are related, but not necessarily clear on first glance.  One is the idea that you should never let your entities enter an invalid state, the other is that you should get rid of setters on your entities altogether.

I won’t claim to understand all of the implications this has if you are doing full-blown DDD, but within DDD-Lite, I think I can give an example of what is involved.  YMMV.

Suppose you have a (very simplistic) Address class that has a City property and a Zip Code property.  You will want to be able to validate that these two properties match in your application.  But there is an immediate circular dependency problem here.

Suppose you create an instance of your Address class and set those properties.  So (very naive implementation):

Address a = new Address();

a.City = “Valparaiso”;

a.Zip = ‘46383’;

Ignoring the obvious difficulties, let’s assume there isn’t a validation problem here (though there is, as we’ll see).  Suppose that you want to update the Address so that the City is ‘Chicago’ and the Zip is ‘60618’.  One might think you can do it this way:

a.City = “Chicago”;

a.Zip = “60618”;

But, if you change the City property first, unless you do validation, your entity is invalid, as the City = “Chicago” does not validate against the Zip of “46383”, which is what the Zip is at the time of the change.  It needs the Zip to be changed so that Zip = “60618” to be valid.  But, if you try to set the Zip = “60618” first, your entity is invalid since that does not validate against the City of “Valparaiso”.  So, you can’t change one without the other at the same time before you do validation.

The first time I read something about this (long before lurking on the DDD list), I had the immediate, gut-check reaction: this is just stupid.  You have to be able to make multiple changes.  You will generally do this from some UI screen anyway, so you will pass in all of those changes in as a bunch.

But this side-steps the issue.  In your batch, as you change the properties one by one, your entity can’t do validation on each property change, instead it has to do validation on the entire batch.  Immediately, you have the difficulty of managing this.  If your entity has property validation, you have to make sure it doesn’t do this outside of the entire batch, so you have to write code to manage this. 

More importantly, if your entity has setters for City and Zip, and these perform validation, there is nothing to prevent client code that has access to your entity from trying to set these properties individually.

Okay, so how the hell else are you going to do it?  This is where eliminating setters comes in.  You prevent client code from being able to set individual properties at all.  Instead, to change an Address, you have to pass in the ‘full batch’.  The mechanisms by which you could do this are many, so let’s examine a simple one.  First, to prevent the initial problem with validation, you force the constructor for an Address to take in a batch:

Address a = new Address(“Valparaiso”, “46383”);

Your constructor enforces validation on creation.  Then, to change the Address, you pass in the batch this way (listing the constructor again):

Address a = new Address(“Valparaiso”, “46383”);

Address new = new Address(“Chicago”, “60618”);

a.ChangeAddress(new);

The ‘ChangeAddress’ method takes in the entire batch of a different address, and so can perform validation on the batch.

Why?  Really, why do this?

If you eliminate setters and allow only methods that attempt a batch update, then you no longer need to check the validity of an entity.  So what?

Well, think about what you might typically need to do if you are passing an entity between tiers.  At each tier, you might need to do an if (entity.IsValid()) check before proceeding.  What’s the likelihood you will forget to do this check? 

If you prevent an entity from ever becoming invalid, you relieve yourself (and your codebase) from having to worry about this.

Summation

This example is incredibly simplistic.  But, imagine that the class isn’t of type Address, but of type Order, or of type TradeRequest, etc.

Always force your entities to do validation on creation, so that you cannot create an invalid entity.  Eliminate setters, and replace them with method calls that will perform validation on a batch update.  This will also prevent you from changing a valid entity to an invalid one.  By doing so, you elminate the need to perform validation checks across your application.  In this simplistic example, you pass in entire entities to your change requests, but you will probably do this by passing in messages or commands.

Regardless, the point is the same.  By eliminating property setters, you eliminate the risk of having invalid entities at any point in your application.

posted on Friday, February 20, 2009 9:27 PM Print
Comments
Gravatar
# re: DDD-Lite: Mama don’t let your entities grow up to be invalid
Troy Tuttle
2/23/2009 4:02 PM
I think you are mixing some DDD concepts here. I know your address class is just an example. But according to Evans, your address class is really a value object, not an entity. That may seem like a minor distinction, but it is relevant to your setters vs. constructor argument. The main difference between a value object and entities is value objects are immutable.

So, IMO, your conclusion is correct, but for the wrong reason. If you need new values for your 'value' object, then constructing a new address object with the correct values and assigning it to the customer 'entity' object would be the correct approach.

I am not a DDD expert, that's just my understanding, FWIW.
Gravatar
# re: DDD-Lite: Mama don’t let your entities grow up to be invalid
jdn
2/23/2009 4:14 PM
Actually, my conclusion holds for Customer as well, even if you are changing their name. But you are quite right, using Address as the example is poor for the reason you state (although an Address could be an entity in the right domain, it would normally be a value object), so I'll update the post.

Thanks.
Gravatar
# re: DDD-Lite: Mama don’t let your entities grow up to be invalid
Ryan
2/27/2009 2:24 PM
Read the second comment on this post:
http://www.lostechies.com/blogs/derickbailey/archive/2009/02/15/proactive-vs-reactive-validation-don-t-we-need-both.aspx

Bellware always finds ways to take shots at you.
Gravatar
# re: DDD-Lite: Mama don’t let your entities grow up to be invalid
jdn
2/27/2009 2:41 PM
I didn't really read it that way. Besides, I agree with the following:

"Validation goes where it's needed, but validation isn't owned by merely one class of design patterns. You can, should, and must put validation where validation belongs without dragging along entities for the ride because it's not the entity you always need, it's the validation."

I'm going to end up doing another post instead of updating this one. In a minute or two.

Post Comment

Title *
Name *
Email
Url
Comment *  
Please add 3 and 5 and type the answer here: