A bit more than a week ago, my brother sent me a couple of links about DCI. The vision paper, a presentation by James Coplien and some other stuff. There were immediately some things that rang true to me, but there were also things that felt weird – in that way that means that either I or the person who wrote it has missed something. Since then, I’ve spent a fair amount of time trying to understand as much as I can of the thoughts behind DCI. This post, which probably won’t make a lot of sense unless you’ve read some of the above materials, is a summary of what I learned and what I think is incomplete about the picture of DCI that is painted today.
The Good
I immediately liked the separation of data, context and roles (roles are not part of the acronym, but are stressed much more than interactions in the materials I’ve found). It was the sort of concept that matches your intuitive thinking, but that you haven’t been able to phrase, or perhaps not even grasped that there was a concept there to formulate. I have often felt that people attach too much logic to objects that should be dumb (probably in an attempt to avoid anemic domain models, something I, apparently like Mr Coplien, have not quite understood why it is such a horror). Thinking of objects as either containing data or playing some more active role in a context makes great sense, and makes it easier to figure out which logic should go where. And I second the opinion that algorithms shouldn’t be scattered in a million places – at least in the sense that at a given level of abstraction, you should be able to get an overview of the whole algorithm in some single place.
Another thing I think is great is looking at the mapping between objects in the system and things in the user’s mental model. That mapping should be intuitive in order to make it very easy to understand what to do in order to achieve a certain result. Of course, in any system, there are a lot of objects that are completely uninteresting for – and hopefully invisible to – end users, but very central to another kind of software users: programmers. Thinking about DCI has made me think that it is central both to have a good mapping between the end user’s mental model and the objects that s/he needs to interact with and the programmer’s mental model and the objects that s/he needs to interact with and understand. And DCI talks about dumb data objects, which I think is right from the perspective of creating an intuitive mapping. I think most people tend to think of some things as more passive and others as active. So a football can bounce, but it won’t do so of its own accord. It will only move if it is kicked by a player, an actor. It probably won’t even fall unless pulled downwards by gravity, another active object. It’s not exactly dumb, because bouncing is pretty complicated, it just doesn’t do much unless nudged, and certainly not in terms of coordinating the actions of other objects around it. The passive objects are the Data in DCI, and the active objects are the algorithms that coordinate and manipulate the passive objects.
The notion of having a separate context for each use case, and ‘casting’ objects to play certain roles in that context, feels like another really good idea. What seems to happen in most of the systems I’ve seen is that you do some wiring of objects either based on the application scope or, in a web service, occasionally the request scope. Then you don’t get more fine-grained with how you wire up your object graph, so typically, there are conditional paths of logic within the various objects that allow them to behave differently based on user input. So for every incoming request, say, you have the exact same object graph that needs to be able to handle it differently based on the country the user is in, whether he has opted to receive email notifications, etc. This is far less flexible and testable than tailoring your object graph to the use case at hand, connecting exactly the set of objects that are needed to handle it, and leaving everything else out.
The Not Great
The thing that most clearly must be broken in the way that James Coplien presents DCI is the use in some languages of static class composition. That is, at compile time, each domain object declares which roles it can play. This doesn’t scale to large applications or systems of applications because it creates a tight coupling between layers that should be separated. Coplien talks about the need for separating things that change rarely from those that change often when he discusses ‘shear layers’ in architecture, but doesn’t seem to notice that the static class composition actually breaks that. You want to have relatively stable foundations at the bottom layers of your systems, with a low rate of change, and locate the faster-changing bits higher up where they can be changed in isolation from other parts of the system. Static class composition means that if two parts of the system share a domain class, and that class needs to play a new role in one, the class will have to change in the other as well. This leads to a high rate of change in the shared code, which is problematic. Dynamic class composition using things like traits in Scala or mixins feels like a more promising approach.
But anyway, is class composition/role injection really needed? In the examples that are used to present DCI – the spell checker and the money transfer – it feels like they are attaching logic to the wrong thing. To me, as a programmer with a mental model of how things happen in the system, it feels wrong to have a spell checking method in a text field, or a file, or a document, or a selected region of text. And it feels quite unintuitive that a source account should be responsible for a money transfer. Spell checking is done by a spell checker, of course, and that spell checker can process text in a text field, a file, a document or a selected region. A spell checker is an active or acting object, an object that encodes an algorithm, as opposed to a passive one. Similarly, an account doesn’t transfer money from itself to another, or from another to itself. That is done by something else (a money transferrer? probably more like a money transaction handler), again an active object. So while I see the point of roles and contexts, I don’t see the point of making a dumb, passive, data object play a role that is cut out for an active, smart object.
My Thoughts
First, I haven’t yet understood how Dependency Injection, DI, is ‘one of the blind men that only see a part of the elephant’. To me, possibly still being blind, it seems like DI can work just as well as class composition. Here’s what the money transfer example from the DCI presentation at QCon in London this June looks like with Java and DI:
interface SourceAccount {...} interface DestinationAccount {...} class SavingsAccount implements SourceAccount, DestinationAccount {...} class PhoneBill implements DestinationAccount {...} public class MoneyTransactionHandler { private final GUI gui; public MoneyTransactionHandler(GUI gui) { this.gui = gui; } public void transfer(Currency amount, SourceAccount source, DestinationAccount destination) { beginTransaction(); if (source.balance() < amount) { throw new InsufficientFundsException(); } source.decreaseBalance(amount); destination.increaseBalance(amount); source.updateLog("Transfer out", new Date(), amount); destination.updateLog("Transfer in", new Date(), amount); gui.displayScreen(SUCCESS_DEPOSIT_SCREEN); endTransaction(); } }
This code is just as readable and reusable as the code in the example, as far as I can tell. I don’t see the need for class composition/role injection where you force a role that doesn’t naturally fit it upon an object.
Something that resonates well with some thoughts I’ve had recently around how we should think about the way we process requests is using contexts as DI scopes. At Shopzilla, every page that is rendered is composed of the results of maybe 15-20 service invocations. These are processed in parallel, and rendering of the page starts as soon as service results for the top of the page are available. That is great, but there are problems with the way that we’re dealing with this. All the data that is needed to render parts of the page (we call them pods) needs to be passed as method parameters as pods are typically singleton-scoped. In order to get a consistent pod interface, this means that the pods take a number of kitchen sink objects – essentially maps containing all of the data that was submitted as a part of the URL, anything in the HTTP session, the results of all the service invocations, and things that are results of logic that was previously executed while processing the request. This means that everything has access to everything else and that we get occasional strange side effects, that things are harder to test than they should be, etc. If we would look at the different use cases as different DI scopes, we could instead wire up the right object graph to deal with the use case at hand and fire away, each object getting exactly the data it needs, no more, no less. It seems like it’s easy to get stuck on the standard DI scopes of ‘application’, ‘request’, and so on. Inventing your own DI scopes and wiring them up on the fly seems like a promising avenue, although it probably comes with some risk in terms of potentially making the wiring logic overly complex.
In some of the material produced by DCI advocates, there is a strong stress of the separation between ‘object-oriented’ and ‘class-oriented’ programming, and class-oriented is bad. This is a distinction I don’t get. Or, the distinction I see feels too trivial to be made into a big deal. Of course objects are instances of a class, and of course the system’s runtime state in terms of objects and their relationships is the thing that makes it work or not. But who ever said anything different? There must be something that I’m missing here, so I will have to keep digging at it to figure out what it is.
DCI + DI
I think the spell-checker example that James Coplien uses in his presentations can be used in a great way to show how DCI and DI together make a harmonious combo. If you need to do spell checking, you probably need to be able to handle different languages. So you could have a spell checker for English, one for French and one for Swedish. Exactly which one you would choose for a given file, text field, document or region of text is probably something that you can decide in many ways: based on the language of the document or text region, based on some application setting, or based on the output of a language detector. A very naive implementation would make the spell checker a singleton and let it figure out which language to use. This ties the spell checker tightly to application-specific things such as configuration settings, document metadata, etc., and obviously doesn’t aid code reuse. A less naive solution might take a language code as a parameter to the singleton spell checker, and let the application figure out what language to pass. This becomes problematic when you realise that you may want to spell check English (US) differently from English (UK) and therefore language isn’t enough, you need country as well – in the real world, there are often many additional parameters like this to an API. By making the spell checker object capable of selecting the particular logic for doing spell checking, it has been tied to application-specific concerns. Its API will frequently need to change when application-level use cases change, so it becomes hard to reuse the spell-checker code.
A nicer approach is to have the three spell checkers as separate objects, and let a use-case-specific context select which one to use (I’m not sure if the context should figure out about the language or if that should be a parameter to the context – probably, contexts should be simple, but I can definitely think of cases where you might want some logic there). All the spell checker needs is some text. It doesn’t care about whether it came from a file or a selected region of text, and it doesn’t care what language it is in. All it does is check the spelling according to the rules it knows and return the result. For this to work, there needs to be a few application-specific things: the trigger for a spell checking use case, the context in which the use case executes (the casting or wiring up of the actors to play different roles), and the code for the use case itself. In this case, the ‘use case’ code probably is something like:
public class SpellCheckingUseCase { private final Text text; private final SpellChecker spellChecker; private final Reporter reporter; // constructor injection ... public void execute() { SpellCheckingResult result = spellChecker.check(text); reporter.display(result); } }
The reporter role could be played by something that knows how to render squiggly red lines in a document or print out a list of misspelled words to a file or whatever.
In terms of layering and reuse, this is nice. The contexts are use-case specific and therefore at the very top layer. They can reuse the same use case execution code, but the use case code is probably too specific to be shared between applications. The spell checkers, on the other hand, are completely generic and can easily be reused in other applications, as is the probably very fundamental concept of a text. The Text class and the spell checkers are unlikely to change frequently because language doesn’t change that much. The use case code can be expected to change, and the contexts that trigger the use case will probably change very frequently – need to check spelling when a document is first opened? Triggered by a button click? A whole document? A region, or just the last word that was entered? Use different spell checkers for different parts of a document?
I think this is a great example of how DCI helps you get the right code structure. So I like DCI on the whole – but I still don’t get the whole classes vs objects discussion and why it is such a great thing to do role injection. Let the algorithm-encoding objects be objects in their own right instead.
#1 by ryzam on September 16, 2010 - 22:14
Hi, this is an excellent article, but i think it’s nice to get James comment, you can ask in the object composition group
http://groups.google.com/group/object-composition
#2 by James O. Coplien on September 17, 2010 - 10:24
Hi, Petter,
I’m glad you’re exploring DCI. However, I think you misunderstood quite a few of the foundations, and that might affect your conclusions. For example:
DCI does not depend on static class composition, unless you’re programming in C++ or in Scala, as I presented in examples in some of my talks. Contrary to what you say above, Scala does not have dynamic class composition.
I’m not sure how to get across the fundamental point about the difference between object-oriented and class-oriented. It’s a difficult thing to convey to a Java programmer. Again, this relates to human mental models. Classes figure prominently in the programmer’s mind, and objects figure prominently in the users’ mind. DCI bridges these two worlds. Maybe that’s hard to see when sitting in the programmers’ chair. You might spend some time sitting with a user and doing some analysis of their mental models and of how the program should present them.
I think that to better understand DCI, you might explore the resources at: http://www.leansoftwarearchitecture.com/home/more-online-resources. And write more code; DCI is perhaps best explored in Ruby or Python.
Good luck!
#3 by sebastiankuebeck on September 17, 2010 - 11:06
> First, I haven’t yet understood how Dependency Injection, DI, is ‘one of the > blind men that only see a part of the elephant’. To me, possibly still being
> blind, it seems like DI can work just as well as class composition.
I think you are making the mistake of picking out one piece of DCI and confuse it with the whole thing.
There is more to DCI than object inheritance (parametrized type inheritance is a workaround in languages that don’t support object inheritance such as trais or prototypes).
Try not to get hooked too much on the examples. They contain workarounds to enable DCI style programming in languages that don’t have native trait support.
DCI is a whole design philosophy and object inheritance is just a technique to make a part of it happen.
To DI: Yes, you can use DI to implement the same functionality. You could also use procedural, functional or pure object-oriented style. All those approaches have their pros and cons.
DCI is a very elegant way to implement functionality but you cannot do things with DCI that you couldn’t do otherwise. This is true for any software development paradigm (as long as it is Turing complete of corse).
#4 by Petter Måhlén on September 18, 2010 - 17:00
Thanks for the feedback – I feel honoured to get responses from some of the main people behind DCI!
Also, thanks for correcting my misunderstanding about Scala’s traits, I thought they were dynamic, but that’s obviously not the case. Still, I don’t think that affects the point about static class composition: I don’t think that it scales to larger systems. That may mean that languages such as Scala, Java and C++ are unsuitable for full DCI programming unless your system is small.
I will certainly continue thinking about the distinction you guys are making between object-orientation and class-orientation, which I still don’t get. But I think that it is interesting for people who cannot easily choose a language that allows dynamically assigning roles/functionality to objects that a lot of aspects of DCI seem really valuable anyway. That’s more or less what I was trying to say with the example at the end. Or would you say that using DI instead of the piece of DCI that is class composition means missing the point completely?
#5 by sebastiankuebeck on September 18, 2010 - 17:34
Petter,
just look at Javascript! You can do object-oriented programming with it although it doesn’t have the concept of classes (just ignore the fact that the Chrome V8 engine creates classes in the background for performance reasons – you don’t see them anyway):
http://articles.sitepoint.com/article/oriented-programming-1
http://articles.sitepoint.com/article/oriented-programming-2
This gives you the flexibility of adding methods to objects at runtime and you are not constraint by any class definition.
#6 by James O. Coplien on September 18, 2010 - 18:44
> Thanks for the feedback – I feel honoured to get responses from the
> main people behind DCI!
The main person in DCI miss being mentioned in your article, and you seem to have missed most of his publications on DCI — which are perhaps the most comprehensive, direct, and authoritative sources on the topic. That would be Trygve Reenskaug. While the mano-à-mano discussions here are fun, I’d really commend the written sources to you. I can even plug the new book Lean Architecture, which includes two chapters that introduce DCI and has four or five appendices with examples in different programming languages.
> Still, I don’t think that affects the point about static class composition:
> I don’t think that it scales to larger systems.
1. I would believe that you might be right except we have many working examples of this working in the industry.
2. This has very little to do with DCI per se, as the above comments corroborate.
Best,
Cope
#7 by sebastiankuebeck on September 18, 2010 - 19:00
> Thanks for the feedback – I feel honoured to get responses from the
> main people behind DCI!
BTW: I am not a person behind DCI.
I am just interested in it. ;-)
#8 by Petter Måhlén on September 19, 2010 - 11:27
@Sebastian: exactly – in languages like JavaScript, where you can dynamically add code to objects, I can see injection of roles into objects working.
@James and Sebastian: I intended to write ‘some of the main people’, not ‘the main’, I have updated the comment. And Sebastian, your blog is mentioned prominently in the DCI documentation, so you’ll forgive me for making that mistake. :)
@James re point 1 above: I should be clearer about the reason why I don’t think that static class composition as described in the DCI examples scales to large systems: the things that need to change quickly are of course use cases/contexts, then roles, and somewhere at the bottom are the domain objects. This translates into layers, where a layer below knows nothing about the layer above. If the core domain objects need to know about every role they can play in the system, as the examples of static class composition indicate, then the layering collapses and you lose the advantage of having differentiated rates of change. You can launch a production system without that differentiation, but code reuse and system evolution will be painful.
I’ve realised that there is (at least) one way to get working layering with static class composition, though: instead of having the actual domain model class instantiated as the objects that play various roles, there could be proxy classes defined in one of the top layers of the system. These proxies would delegate the domain logic to an instance of the actual domain object, and would essentially correspond to “a list of the roles the domain object can play in this part of the system”. It’s a bit of extra plumbing, but it could work.
@James re point 2 above: That’s exactly my second point – it seems to me that a large part of DCI doesn’t require class composition. The spell checker example in the post gives me reason to think that DCI+DI is a very promising avenue to explore with regard to some problems that I am seeing in my day job and that you’re referring to in your talks: algorithm “explosion”, getting layering right, and simplifying the objects involved in the algorithms by removing conditional/extraneous logic through doing more precise wiring up in the contexts (I don’t believe you mentioned that last point, but it is relevant to quite a few scenarios I encounter frequently).
It is very unlikely for a large number of reasons that I will be abandoning Java in the near future. But it is very likely that the next system design I work on will be heavily influenced by the thoughts in DCI.
#9 by James O. Coplien on September 19, 2010 - 15:49
> @James re point 2 above: That’s exactly my second point –
> it seems to me that a large part of DCI doesn’t require class composition.
I don’t think that was your point. Your point was that what DCI does can be done by DI, which I don’t think is true. There is no need for a discovery or claim of DCI not needing class composition; the majority of published examples, to my knowledge, already lack class composition.
Much of the semantics of DI is already present in our implementations of DCI, so I do not understand at all what you mean by combining them. Dependency injection is about relaxing static dependencies, and it solves the problem at the expense of object identity. Most implementations based on dependency injection (e.g., Elmo or ObjectTeams) end having the problem of self-schizophrenia. If you look at the Wikipedia definition of DCI, it requires that the solution retain object identity. One reason for this is that issue of object programming that you are struggling with. But the other is much simpler: the identity of the injected object and the injectee are not in general interchangable, and this leads to a homogeneous mixture of these two identities in a single program. In general, such a program will simply not work.
Solving this problem was the last bit of the puzzle in getting DCI to work. It’s why we use traits in general, which tends to look like class composition in the world of classful languages, and which tends to use more dynamic MOPs in real programming languages. As you pointed out, Java has neither, so Java needs to be augmented to make it into a higher-order language like Qi4j.
Maybe you should have a look at Qi4j: it may be what you need to make the intellectual breakthrough that still seems elusive.
#10 by Petter Måhlén on September 20, 2010 - 08:00
Thanks, that was a very enlightening comment. Let me rephrase the second point I was trying to make once more (I think I alone can determine what point that is, although I’ll grant that I have probably not been able to formulate it as clearly as could be). The point is that as far as I can tell, the problems that are described in the vision paper and in your talks and that DCI solves can equally well be solved using the Data, Context and Interactions concepts and replacing class composition with dependency injection. Instead of injecting algorithms into objects as in DCI, you inject objects into algorithms. To borrow terminology from the vision paper, the dichotomy between what the system is and what it does is solved not by combining those two into a single object, but through having two different objects deal with it. Because they are two things, there is no self schizophrenia, as I understand your Wikipedia article on the concept. The DCI+DI solution seems simpler and more intuitive to me, and simplicity is a great thing. I think that for DCI (with class composition) to truly catch on as a superior solution, you need to refute this point, and I’d love to see you do that.
Self schizophrenia seems to be what you would get with my proposed solution for dealing with the layering problem introduced by static class composition, though. The top-layer proxy objects that would delegate some of their work to domain objects seem to exhibit exactly that, although I am not sure if that is a problem in practice.
I had looked at Qi4j a year or so ago and again just before writing this post. Both times I felt the same: while I can appreciate where they are trying to go and would probably like to use a framework like that, the syntax that they have achieved makes me feel Java isn’t a great choice as a language to implement that type of framework. (Maybe because it’s not “real”.) But it’s definitely a technology I am watching.
I will continue reading about this, it is certainly interesting and I’m learning lots of stuff. And, who knows, maybe one day I will make an intellectual breakthrough! ;)
#11 by awgtek on May 25, 2012 - 21:33
” Instead of injecting algorithms into objects as in DCI, you inject objects into algorithms.”
Greetings Petter! Did you ever get validation for this idea? I too have a problem with “injecting algorithms into objects” and I agree it should be reverse. I like your football analogy BTW.
#12 by Petter Måhlén on May 28, 2012 - 09:39
I wrote a follow-up post to this one about that topic: https://pettermahlen.com/2010/10/02/dci-better-with-di/. It didn’t meet with much appreciation among the DCI community, so I can’t say those ideas have been validated. :) I personally think there’s something there, though, and that a lot of the DCI examples/discussions I’ve seen make mistakes such as giving contexts too many different responsibilities, etc.
In practice, these days I occasionally find myself writing code that uses dynamic dependency injection as a means to keep responsibilities separated and make classes simpler. So it’s a tool I’m using at least.
#13 by James O. Coplien on September 20, 2010 - 08:31
> Self schizophrenia seems to be what you would get with
> my proposed solution for dealing with the layering problem
> introduced by static class composition, though. The top-layer
> proxy objects that would delegate some of their work to
> domain objects seem to exhibit exactly that, although I am
> not sure if that is a problem in practice.
I wish that some day Trygve would publish his horror stories of the problems that self schizophrenia caused in the nascent implementations leading up to DCI. It’s a real problem.
I also had a client in Austria who was using your approach, and we uncovered a fatal flaw in their architecture during an architecture review I did there. It had been causing them problems for years in a large, complex system, and they never had been able to find it.
The Traits puzzle piece is, to me, the shining diamond of DCI. Most people intuit that it is impossible to do anything like it and so they dismiss it out of hand. Just look at the old discussions on object-composition surround ObjectTeams. One of the documents I read (I don’t remember if it was ObjectTeams or Elmo) said that self schizophrenia is “not often” a problem. I want my software to work correctly *all* the time, not just often. But this good-enough mentality seems to be sweeping the software world — probably because of the web, whose intrinsic diminished reliability masks the underlying sloppiness.
This stuff is hard. Trygve has been working on it in some form for about a decade and I have been working together with him for three or four years, and with other people like Sebastian more recently. We’ve been over these kinds of issues again and again and again with long deliberation and hundreds or thousands of eyes poring over it. We’re of course always looking for newly uncovered pitfalls and of ways to do them better. Let me encourage the readers out there to contact us if you discover DCI improvements or pitfalls — before publishing it in a ‘blog after reading two or three papers and spending a few days putting together code using known techniques that do not arise to the occasion.
> I had looked at Qi4j a year or so ago and again just
> before writing this post. Both times I felt the same:
> while I can appreciate where they are trying to go and
> would probably like to use a framework like that,
> the syntax that they have achieved makes me feel
> Java isn’t a great choice as a language to implement that
> type of framework.
To loosely quote the rationale given by a noteworthy ‘blogger of our times: I hear that it’s unlikely, for a large number of reasons, that they will be abandoning Java in the near future. :-)
#14 by awgtek on June 4, 2012 - 20:04
Just a follow-up (thanks for your answer to my comment above), I just found this: http://heim.ifi.uio.no/~trygver/2006/09-JavaZone/mvc-dca.pdf , which apparently shows Trygve making use of discreet algorithm classes in addition to collaberation classes, in “DCA.” Apparently Trygve abandons this idea of a standalone algorithm entity in favor of assigning it to objects (i.e. DCI role methods). I’m not sure what the rationale for that was…perhaps to satisfy the OO requirement that behavior be attached to objects? But if that’s so, that contradicts the above paper where he states putting the frontload algorithm in a datamodel object is problematic because “The method cannot be called in an activity object before the
earlyFinish of all predecessors are known.” I suspect that DCI is a runtime fix, but I haven’t heard validation for this.
#15 by Petter Måhlén on June 5, 2012 - 10:17
You can just ask him directly. :) Post a question to https://groups.google.com/group/object-composition, and you’re sure to get some interesting responses.