Sunday, March 18, 2012

More Notes on DCI

I have been really interested in Data Context Interaction (DCI) for some years now. Before I go any further, I will direct readers to a comment by Bill Venners in a discussion from 2009. Here are some key quotes from that:
DCI ... challenges a basic assumption inherent in the notion of an anemic domain model. The assumption is that an object has a class.
DCI challenges the assumption that each object has one and only one class. The observation is that objects need different behavior in different contexts.
This is really important, and it highlights a distinction that we need to draw between objects and class instances. We will talk about that more later.

If some of the above terminology is unfamiliar, Fowler has a decent explanation of anemic domain models. I do not completely agree with some of the discussion (including by extension some of what Eric Evans, who is quoted by Fowler, has to say) but the explanation of anemic domain models is good.

The idea that class-oriented OO has not been serving us well is not new. The DCI folks (Reenskaug, Coplien etc) certainly think so. What is class-oriented or class-based OO exactly?

For most programmers this question is most easily answered by explaining that there are other styles of OO programming that have no classes at all. I refer you to Wikipedia's article on prototype-based programming. These days it is a safe bet that many programmers have used at least one language that allows prototype-based OO: JavaScript. The fact that the JavaScript tends to be used as small snippets in web pages obscures the more general utility of this capability in the language itself. After all, JavaScript is a full-fledged language; it is not just for browsers.

Most programmers use OO languages that are fully class-based: Java, C#, C++, among others. We get objects by creating new instances of classes. All objects that are instantiated from the same class "template" obey the same contract for behaviour, and have the same invariants. Only the instance state can vary.

Where the problem arises is in determining where to place behaviour. Some behaviour clearly belongs in one domain class, and no other. It is reasonable, for example, that a Shape object can report its area and perimeter and other geometric facts. It is reasonable that a Matrix can calculate its own determinant or eigenvalues.

But what about business logic that involves several classes? Fowler and Evans tell us that the domain layer contains business rules, and that the domain objects represent business state. Their picture of the application or service layer is clearly such that "business logic that involves several classes" should be in the domain layer. OK, in which case what class do you put that in? Does class A get the method and it accepts parameters of type class B and class C? Or does class B get a method that accepts parameters of type class A and class C? You get the idea.

No, what happens almost inexorably is that programmers write a method that is supplied with parameters of type class A, class B and class C, and that method ends up in a "service" class (often a session bean in J2EE/Java EE). And a method of that sort is usually quite procedural.

If you like you could consider these objects as being in the domain layer. They are unlikely to have state per se, but if they do you could call it business state. But nevertheless these classes are merely libraries of procedural code; the logic simply happens to act on objects, but it is still procedural code. The end result is still what Fowler and Evans are warning against.

But in the absence of something like DCI, writing this procedural code is probably the most acceptable thing for a real-world programmer to do. In fact, if done well it actually captures use cases much better than trying to assign all business logic into domain class methods.

For me, DCI is a way of codifying this existing practice, and rather than demonizing it, giving it formalism and structure. For largely the wrong reasons - ignorance if nothing else - OO programmers naturally arrive at the notion of barely smart data (domain classes with minimal behaviour), and things that look like contexts (procedural classes that embody use case logic). Exactly the kind of thing that Fowler warns against.

What has been lacking is system. The ad hoc procedural code that programmers stumble into is rarely cohesive or well-organized or purposeful. It is not easy to find code in one location that applies to one use case. DCI sets this on a firmer footing.

I think it will be interesting to see how this all falls out. There really has not been much uptake for DCI, and the ideas have been around for about three years. Part of the problem is that DCI is quite difficult to do in Java, and is cumbersome and inelegant in C# and C++. It is straightforward in Scala, but few people use Scala. Ruby is a natural fit, and a lot of the applications that Ruby is used for are conducive to DCI, but the adoption rate has been slow. Other dynamic languages also support DCI without too much fuss, like Perl or JavaScript, but the adoption rate is also slow.

One problem - and I am not being facetious - is that the canonical example advanced by the DCI camp, which is the accounts+ money transfer example, is atrocious. It is strained and awkward and Godawful. No wonder a lot of programmers have been turned off DCI. Unfortunately it is still by far the #1 example that is to be found on the Web. I hope this changes.

I hope, that if this is the first mention of Data Context Interaction that you have run across, that it interests you in further reading. I think it is a valuable technique to study. Think through your past OO experiences with rich domain objects, and your problems in placing behaviour that really seems to be algorithmic rather than belonging to an object, and see if DCI cannot help you further along the road to solid, reliable, readable code.

No comments:

Post a Comment