DCI Architecture is Visionary

Welcome in the 6th installment of my architecture series. So far we have covered 5 similar architectural styles, from the Layered Architecture to the Clean Architecture. In this article, we’ll look at something substantially different – the DCI architecture by James Coplien and Trygve Reenskaug.

What is DCI?

As I said before, the DCI architecture is different than the 5 styles that we previously discussed. The main difference between DCI and the rest is that it tells you (close to) nothing about layers and code organization, in the sense of what package should your classes go into. So what does it tell us?

DCI stands for Data, Context, and Interaction. This name should resonate with the way most people understand object-orientation. We have some context i.e. a user’s incoming request, we retrieve some data i.e. the objects and we make those objects interact to satisfy the user’s request. So far everything should feel common. Let’s move to the uncommon part.


The Data part of DCI consists of domain objects à la entities in DDD or Clean Architecture. The major difference between DCI‘s data object and a typical entity is that the data object is relatively stupid. It’s not anemic. It can still contain important domain methods that would preserve it’s invariants etc., but it shouldn’t contain a single method that is specific to an application’s use case and not the domain itself. If this feels blurry, think about the concept of printers that some people promote:

They claim that this approach to serializing objects is better because it promotes better encapsulation and hence better object-orientation. In DCI‘s data, such approach could not take place since printing is part of a use case and not a part of the domain object.


So where does the use case-specific code go? The answer is: in the object roles.  The concept of roles is pretty unique to DCI – I haven’t seen it in any other major architectural style. The roles are supposed to dynamically extend the data object’s behavior with the use case-specific functionality. Since such functionality might want to operate on object’s state (like the printer above), an object role should preferably have access to the object’s internals via accessors or simple methods. In pseudo-code, our example with an extracted role could look like this:

I see two major benefits of this approach. First, the domain object is not cluttered by peripheral stuff that it would not contain if not for some use case. Second, an object role can be played by objects of different classes, as long as they contain data and methods necessary to fulfill the role. In our example, we could print a magazine the same way we print a book (we should adjust the role‘s name then).


The place where data objects are retrieved and roles assigned is called the context. This would be a rough equivalent of an application service or Clean Architecture’s use case interactor. Why only rough? Because ideally, a context should provide the roles with references to all collaborators in a use case, call one of the roles and do nothing else.

Think about a bank transfer. We have two accounts – source account and destination account. Obviously, the account is a data object, while source account and destination account are roles. When the source account decreases its own balance, it wants to let the destination account know that it should increase its balance. Once this is done, a success message is shown in the GUI. Does that mean that the SourceAccount should carry around a destination account and a GUI reference? And if there were more collaborators, should each of them contain a reference to each one it collaborates with. That could easily get out of hand. Hence, DCI assumes that object roles should know what collaborators they have based on the context in which they execute.

All Together

Let’s walk through things the way control flows in the application. A user presses a button that sends a use-case-related request. An application receives that request, instantiates an appropriate context, and passes the request to it. The context retrieves all data objects necessary to fulfill the use case‘s goal and assigns appropriate roles to them. Then, it sends a message to an object role that begins a series of interactions between the objects. If there is a need to communicate something to the user, it’s also done by the roles. Once it’s complete, the user’s goal should be achieved.

The Essence of DCI

I see two special things about DCI: separating the stable from the ever-changing and having real networks of collaborating objects.

Separating the Stable

The idea of extracting the use case-specific logic to separate constructs called object roles might seem odd to you at first. So far I said that it prevents clutter and allows for better code reuse. That’s all true, but at a class level. At the perspective of the whole architecture, it has a far more profound meaning. We are separating things that change at different rates and for different reasons (Single Responsibility Principle, anyone?!).

Think about it. If you get your domain model right, or rather right enough, it should not change unless the domain itself changes – it’s STABLE. On the other hand, new use cases or parts of them that operate in this domain can flow in and out all the time. Businesspeople can change their mind. Add this feature, delete that, change something. The use case stuff is EVER-CHANGING and it changes mostly because people change their mind. Have you ever heard developers complaining that the architecture should provide a stable foundation for future development and it doesn’t do so? Well, it’s hard to achieve that if you keep mixing things up.

Networks of Collaborating Objects

It might seem surprising to you that I pointed it out. You know, isn’t it what the whole object-orientation is supposed to be about? And yet we keep writing code, in which communication looks like this:

There’s no collaboration. It’s just the service, calling methods one by one. In DCI, the communication would look like in the picture presented in the All Together section.

Implementing DCI

If you have thought all this time that such magic is impossible in Java, you were partly right. There is a project known as Apache Polygene, formerly known as Apache Zest and Qi4J. It allows you to mix classes into each other in the DCI-way, but the code produced when working with it is at least complex. For this reason, we’ll look at a Marvin example instead:

As you can see, in Marvin the roles are defined inside the context and actual objects are assigned to them the same way you’d assign fields. To initiate the use case we call the Trans() method, which calls the first role: Source. The source account is responsible for decreasing its own balance and telling Destination to increase its balance. For the example to work, the Account  obviously needs to implement the IncreaseBalance and DecreaseBalance methods:

Of course, this is a toy example, so the whole DCI-thing looks more complex than the domain itself. Unfortunately, there aren’t any non-toy examples online or I haven’t found them. If you want more like this one, including even some Java code, you can check them out on the DCI page.

Benefits of DCI

Since I haven’t seen or heard about a DCI application running in production, we will look at its theoretical benefits.

  • Screaming++ – it doesn’t highlight just the use cases, it also highlights the roles that objects play in these
  • Even higher cohesion and lower coupling – there are no methods that take care of single use case peripheral concerns in your domain objects
  • Lean – by putting the stable at its core, it helps to eliminate unnecessary rework in the process of adding features to the application
  • Plays well with DDD – the domain model has an important place in the architecture, and it’s pure and clutter-free

Drawbacks of DCI

  • Learning Curve – the concept is pretty complex and there aren’t too many good learning materials for it
  • Lack of good tools – it’s not really supported by any major programming language. You need to work around with existing mechanisms or niche tools like Polygene
  • Heaviness – it’s not an architecture for simple applications like CRUDs

When to use DCI?

Initially, I wanted to put here some shallow advice that the team needs to know the architecture and the tools etc. But I changed my mind. If you’re working for a company with over 5 employees in the Java field, don’t go there. Even if your team feels good enough to pull it off, there’s a high chance that you will produce something incomprehensible for your successors. And all the rework time saved by DCI, you’ll spend struggling with niche tools and lack of community support. I imagine that the situation could look a little different in other languages, but I can’t say for sure.


DCI changes the way in which we think about object-orientation on the architecture level. By separating roles out of objects and making them communicate in contexts, we create real networks of cooperating objects. The data part of our application, where the static picture of our domain lies, remains stable and uncluttered in the process of adding new features. Unfortunately, this cool vision suffers from a lack of good tooling and learning resources, especially in Java. But even if you shouldn’t go DCI in your projects right now, it’s an architecture worth knowing and remembering.

If you want to learn more about architecture and DCI, check out its Wikipedia page or the excellent book Lean Architecture for Agile Software Development.

About the Author Grzegorz Ziemoński

King of Tidy Java, nerd that thinks about producing perfect software all the time and proud owner of 2 cats.

follow me on: