Tuesday, June 2, 2009

Once upon a time, there was a fledgling young developer. He wrote a nice, clean business object.

publicclass NiceCleanBusinessObject{publicstring Value { get; set; }

publicstring LastUpdatedBy { get; set; }

public DateTime? LastUpdatedOn { get; set; }}

He liked to share, so one day, he decided to use WCF so his business object can be shared with other people. Not seeing a point in having a separate DataContract class from his business object, and wanting to save some time, he decorated his business objects with DataContract and DataMember attributes so he could avoid writing a translator class, which he considered an extra level of abstraction. His nice, clean business object then looked like this:

The next day, his boss expresses interest in creating a holder class to transport several nice clean business objects across the wire. These business objects should be on a list in another object. Our hero creates this class:

The next day, he gets a request to create a new property, Value2. Value2 is set whenever Value is set, but can also be overridden by the user after Value is changed. But whenever Value is changed now, information from the NiceCleanParentObject must be used to set the information on Value2. So his code now looks like this:

He compiles his code, and it won't run properly. Oh no! What shall he do? Every time he tries to pass the object over WCF, he gets a NullReferenceException. In hunting it down (which takes significantly longer than it should), it turns out that while serializing and deserializing a property, the business logic associated with the property actually gets executed. This is because setters and getters are actually called whenever the DataContractSerializer is running, and it's starting to affect his business object's data. He has a couple of options at this point:

1. He can serialize just the fields and automatic properties. He doesn't particularly like this idea, because other people are relying on the DataContract at this point, and relying on it to be in a particular format. This could become a little sticky for them. 2. He can create a base class that carries a flag that determines the current state of the object, whether it's being serialized or not. He decides this is a good idea. 3. He could try to serialize the parent as well. This has the advantage of giving access to the parent properties, but it's still relying on the implemented logic of the DataContractSerializer, in hoping that the Parent object's properties that are requested to be used are serialized before the List of NiceCleanBusinessObjects. Also, serializing the Parent property, when it carries a property containing a list of children creates cycles in the serialization process, because the parent serializes the children, which serializes the parent and so on so forth, so a workaround has to be made. Nevertheless, his UI architecture is depending on the fact that this Business Object self-updates itself whenever a property is set, so he still needs a reference to the parent. Our hero finds his workaround here.

Oh wow. This isn't so much of a nice clean business object anymore, now is it?

Well, I wish I could post more, but our time is short, so I'll tell you what ultimately happened. The business logic inside the business objects became so complicated, due to the fact that the data contract is the business object itself, that our hero ultimately decides that it would be better to switch to an anemic domain model, blasted by such architecture greats as Martin Fowler and several others.

Ultimately, he, and his team members have to support this model that is the result of his bad decision early on in the project, and he's had to learn a difficult lesson:

Don't make your business objects into data contracts. The translation layer is less work in the long run.