Posts
1150
Comments
891
Trackbacks
1
DDD-Lite: Mama don’t let your objects grow up to be invalid (take two)

So, in a previous post, I was making a point about preventing classes from being invalid by getting rid of setters (public setters at least).

But, I did it in a way that was misleading, or at least could be.  For one thing, I used the term ‘entities’ in the title of the post.  Why was this bad?  Well, within DDD, there is a distinction that is often made between objects that can be called “entities” and those that can be called '”value objects.”  And the purposefully simplistic example that I used (that of an Address class) is normally considered to be a value object and so the title of the post seemed to be inaccurate.  Moreover, it made it seem as if the general point really only applied to value objects, when in fact, it has as much, if not more, to do with entities.

What is the distinction between an entity and a value object?  Read the book.  Thanks for coming, good night!  No, no, that won’t do.  Though there are different ways of laying out the distinction, one way of making it is to say that a value object can be defined in terms of the collection of its attributes, while an entity is something that has an identity over and above its attributes.  The basic idea seems simple enough: if two Addresses have the same street address, city, state and zip code, they are in fact the same Address, while two Customers with the full name of John Smith can still be two distinct Customers.

digression: but it isn’t really that simple.  The concept of identity is one that is very difficult to examine adequately.  It is a hard subject, very hard, painfully viciously hard.  Okay, maybe it isn’t that hard, but it ain’t easy.  Consider the concept of personal identity.  What makes you you?  In common parlance, we want to say that someone, in some meaningful sense, maintains their identity through time, but how do we, well, identify that which defines one’s identity?  One’s body changes through time, so that can’t be it.  Maybe it is the chain of memories that we have, but one’s memories change through time.  And what if the chain is broken?  We might want to say of an amnesiac that they are the same person, even though their memories are damaged.  Maybe it has something to do with self-awareness, the phenomenological experience that you have of being a person who persists through time.  Thus, a possibly standard science fiction trope of one’s self-awareness suddenly being moved to another physical body, and the wacky hijinx that ensue.  But one’s self-awareness has broken links.  I go to sleep at night.  Sometimes I dream, but not always. 

It gets (arguably) worse.  Keying on commonly accepted scientific theories and evidence, some philosophers argue that there are, for instance, no such things as beliefs, because you cannot in fact give a solid identification for what makes something a belief as an actual mental ‘thing’.  Instead, there is all this neural activity going on in the brain and we act in certain ways, but there are actually no such things, and the same could be said about memories, so we couldn’t identify personal identity in terms of mental properties, because they aren’t real (the cheap philosophical retort is to ask if these philosophers believe that there are no beliefs).

And it gets (arguably) worse still.  In common parlance, we would say that I am sitting on a chair as I write this drivel.  But what makes that chair the same chair?  If a screw falls out of it, it isn’t normal to say that it is no longer the same chair.  What if I take a chain saw to it?  At what point is it no longer a chair?  Keeping power tools out of the discussion, if we dive down to the particle level, we can’t even identify which particles make up this ‘thing’ that we call a chair, as they are constantly entering and leaving the same physical space (though what makes a ‘physical space’ ‘the same’ as another?).  And we can’t even identify what makes a particle the same particle.

Driven to extremes, the notion of ‘Identity’ seems to break down until, perhaps, it is only things like numbers, arguably the 'quinessential’ value objects, that have a strict identity, which can be defined exactly in terms of its attributes.

Bringing this back somewhat to software development, consider a Customer within a CRM application.  If there are two Customer records that share all of the same relevant (completely ignoring how to define ‘relevance’) attributes save a middle initial (same first and last name, same address, same phone number, same credit card, etc.), we would normally say that this is probably an error condition, that they are the same Customer.  And we determine this by examining the attributes.  Conversely, in a Property Management system, an Address doesn’t seem to be a value object (though it would probably be called a Property or Location, to be sure).

But I digress.

Moreover, in said previous post, I made the point within it based on two mutually dependent properties, but that is only a certain type of validation concern.

So, let’s try this again.  Briefly.

Why should we care about allowing objects to be invalid?

The first point is very simple.  Once you allow an object to be invalid, then at certain points in a system, you will have to ensure that an object is valid to prevent BAD THINGS from happening.  Could be because of interactions with other objects, needing to persist those objects, or a host of other reasons.  At each of these certain points, you will have to remember to do a validation check.  And, I think we can all agree, once there is something you have to remember when developing software, there will be a time and/or a place where/when you will forget. 

Moreover, validation very often involves much more than just ‘simple’ (in terms of lines of code) comparison of one property to another.  It is usually much more along the lines of “Given conditions A, B and C, allow D, E, or F, but not G or H.  But, given conditions A, B, and I, allow D and G, but not H or Q”.  And so on. This is where allowing setters on properties can lead to some difficulty.  With setters, I can change my conditions piecemeal, throughout different parts of the system, and then hope that validation is successful even later on.  But what if it isn’t?  What if validation fails?  Which of those piecemeal conditional changes are the right ones, and which are the wrong ones?  How do you rollback to a valid state?

If you eliminate the possibility of an object being invalid in the first place, you help to manage (note, I didn’t say ‘eliminate’) these issues.  If, at any point of change, you reject a change that will produce an invalid result, you can (potentially) ease the cost of handling the misguided change.  For instance, if instead of changing certain properties of a Customer one by one and then later validating, you pass to your Customer class an ICustomerProfileChange message to it, you have one point of contact, so to speak, one place to handle the validation issues.

Then there is the cost of allowing invalid objects into your system.  Once invalid data is there, how widespread can it get?  A very common task within many environments is data cleanup, fixing all of those errors that have crept in due to various reasons.  There can be a significant maintenance cost involved here.  This is something I deal with at client sites all the time, fixing all of the invalid data spread throughout the system.  If you prevent the source of the data from being invalid, then you can more easily manage (note, I didn’t say ‘eliminate’) this issue.

If you have invalid data being persisted, then you didn’t validate it before you persisted it.

Yes.  But, that is sort of the point.  Someone forgot to check whether the class being persisted was valid.

Elminating setters makes it harder to get things done.

Yes.  But, that is also sort of the point.  By forcing changes to occur in batches (can you tell I come from a SQL/ETL background?), you force changes to be done in consistent chunks. 

So, that’s it?  Eliminate setters everywhere?

Nope.  Blindly following this idea could be bad, for a number of reasons.

By never allowing invalid objects, can’t this increase the cost of maintenance?

Yes.  It can.  ETL projects are an obvious case, but in general, you have to judge the cost of allowing invalid data from entering your system against the cost of blocking the import of data because it has some invalid data.  It could be that the cost of cleaning up, say, invalid Customer data in a CRM system is less than the cost of ensuring the source of the data is valid in the first place.  Allowing an invalid Trade is a different consideration.  Then again, it all depends.  As with anything else, you always have to make judgments about what is better or worse based on the specifics of the system.  Sometimes, you just want to be able to change the First Name of a Customer, and forcing a ICustomerFirstNameChange message is just silly.  Blind adherence to any pattern is problematic. 

Summation

By forcing changes to objects to adhere to specific ‘batches’, you increase the potential of preventing invalid data from entering your system.  As always, you have to consider the costs of doing this before deciding if the cost is worth it, due to cost-savings you will gain elsewhere.

posted on Friday, February 27, 2009 8:32 PM Print
Comments
Gravatar
# re: DDD-Lite: Mama don’t let your objects grow up to be invalid (take two)
Joe
4/15/2010 11:52 AM
How do you handle the case where you're calling an entity constructor (and possibly doing this via a factory) and there are validation errors on multiple parameters? If you throw a ValidationException, you'll only get 1 error and the use will have to go through multiple iterations of submitting data before they get through all the errors. Ideally, I'm looking for a way to elegantly validate each parameter and pass back multiple errors if necssary while still following the "always valid" approach.
Gravatar
# re: DDD-Lite: Mama don’t let your objects grow up to be invalid (take two)
jdn
4/15/2010 7:49 PM
You can always return a list of exceptions, so I'm not sure why there is an issue.

The CQRS way would be to construct a command and invalidate it before it ever got to the constructor/factory.

Post Comment

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