In my previous blog entry, Improving on POCO Properties, we looked at the benefits we can reap for our domain objects if we represent properties with dedicated helper objects instead of restricting ourselves to the usual C# syntax.
As you may have guessed by now, there are improvements that can be made in other areas as well. In this post, the focus will be on composite objects.
As a concept, an Order
is a single item - a request for a set of desired items to be supplied. As a matter of
modelling, the set of items is usually broken out into a list of related OrderLine
instances. Approaching the model in
this manner is very pragmatic, as it allows us to leverage the power of our tooling, but it is important to remember
that an OrderLine
is mostly a modelling artifact.
In C#, our Order
and OrderLine
classes might include details something like this:
Using a simple List to contain the lines provides a minimal set of semantics - lines may be added, removed, and iterated - but nothing more.
Significantly, exposing a list in this manner opens our domain Order
up for external manipulation. Instead of the
Order
having sovereign control over its own information, as you would normally expect, any snippet of code with the
will to do so may change the list of order-lines, even if that would make no sense given the current state of the
object.
One way to address this limitation is to change the declarations to this:
This puts our object in the execution path for modifications to the list of order-lines, but has drawbacks of it’s own.
Importantly, any methods to modify the list of order-lines are no longer found on Lines
, but instead on the parent
object , mixed in with other Order
methods, thus impairing discoverability. The author of the Order
class has to
resolve an implementation tension - either spend a tedious time writing simple code to simulate a full IList
implementation (although without actually achieving IList compatibility), or write a minimal interface that perhaps
won’t meet the needs of consuming code.
Fortunately, there is a better way - by using a specialist helper object to provide the additional semantics we
require. The declaration of Order
simplifies:
Of course, this only becomes interesting when the declaration for DomainCollection
is reviewed:
As you will recall from our previous discussion of the Property
object, the Modified
and OriginalItems
properties
provide for true dirty tracking, though (of course) in this situation the logic required for the test is more complex.
Also included here is full support for transactionality, permitting changes to be committed or reverted as necessary.
Implementing the IList<T>
interface provides for maximum compatibility - even to passing DomainCollection<T>
itself
into methods expecting a simple IList<T>
or even IEnumerable<T>
. Another possibility introduced by this
implementation is the ability to leverage Linq to Objects functionality in useful ways:
Addressing the problem discussed earlier, where external code might manipulate the list in violation of the current
object state, the events ItemAdding
and ItemRemoving
allow the parent object to exert veto rights over any attempted
modification. These events would be hooked when the helper object is created, usually within the constructor of the
parent object.
I hope that you will now be thinking of other ways that the DomainCollection<T>
class might be used to make life
easier. What other ways might simple helper classes make our Domain Model more expressive?
Comments
blog comments powered by Disqus