Plain Old CLR Objects (POCOs), along with their close cousins POJOs (Plain Old JVM Objects) are often held up as the ideal for use when modelling our problem domains.
The arguments in favour of using POCOs are compelling - including independence from any particular object relational mapper (ORM), avoidance of the need to implement a specific interface or to have a certain ancestor object. In fact, the arguments are so compelling that large sectors of the development community take it as a matter of faith that there is no better way.
Early in my career, I worked extensively with a development system that went strongly in the opposite direction - regular objects were considered too simple for an effective domain model. Instead, the system added complex and domain relevant semantics in order to provide a functional and rich modelling environment.
In this blog post, I’d like to begin exploring this approach, bringing it forward thirteen years and looking at how it might impact on the way we do development in 2010 - what knowledge from 1997 have we forgotten and what lessons might we learn again? The original system bound the domain model tightly to the persistence model, with important consequences for the way that business logic was implemented. As we explore this approach, I’ll highlight how persistence modelling was managed, but it’s important to note that the ORM capabilities are really a side effect of the rich modelling support.
As a starting point, consider the humble property. In C#, a class representing a single Person
might look something
like this:
(For a discussion of why this style might be better than the more traditional FirstName/LastName approach, see my earlier blog entry Real World Information Modelling, Part I: Naming).
The semantics of the Person
class are extremely simple - you can set each property, and read the value set, nothing
more.
Let’s introduce a new class Property<T>
to represent each property, turning our class declaration into this:
What does this give us? To find out, let’s look at the declaration of the Property<T>
object itself.
Any property may be null, regardless of the value type. While this would be of lesser value in a modern C# system (given the built in support for nullable types introduced in C# 2.0), this was a significant capability back in 1997.
Keeping track of the original value of the property allows for true dirty checking by detecting when the value has been changed from the original value. This differs from systems which simply record change, by showing the property as unchanged if the original value is restored by the user. Domain level dirty checking provides a number of capabilities, including the ability to generate optimal database updates, updating only database columns with new values to store, and the option to provide visual affordances of modified fields, allowing users to see what they had changed.
Including an implicit cast operator to the value of the property is a convenience for developers using the object, as
it allows them to omit the .Value
when passing property values through to other methods. While this might seem like a
small feature, keeping things easy for the developers using the system is important, else they will be tempted to bypass
the system.
Having a Changed event declared on Property<T>
means that every domain object has that event available for every
property automatically, with no implementation necessary. Going a step further to implement the traditional
PropertyChanged
event normally necessary for databinding is relatively simple.
Lastly, properties can now be transactional - achieving many of the benefits promised by software transactional memory (STM) but with much lower overhead. Depending on the specific implementation, you could even choose to have uncommitted property changes stored per thread, allowing for safe object reuse across threads.
Having seen how our object semantics may be enhanced by using a specific property class, I hope you’ll be thinking about how other areas of our domain might be similarly improved.
Comments
blog comments powered by Disqus