March 4, 2018

DCI in scale-free, fractal way

Complex software applications usually depend on services and libraries; those services depend on other services and libraries, etc. Within an application, in Ruby language, we use modules and classes to divide an application into subsystems and those subsystems into more subsystems, etc. How to do it within DCI concepts? Imagine we have "DCI paradigm only" programming language. It means we don't have "class" or "module," and we can only use keywords like context, data and role. I am trying to understand how DCI can "work in scale-free, fractal way." How can we build a complex application when DCI concepts are built on top of DCI concepts on top of DCI concepts, etc.

At first, I had a small misunderstanding about Contexts in DCI. My original understanding was that Context is a script or a procedure or a method where data becomes objects by playing roles (somehow similar to how OOP in C language works). In that case, context maps to use-case one-to-one. And it still might be true in some cases or a more metaphorical sense. But after discussing with James Coplien, Trygve Reenskaug, Matthew Browne and other members of awesome DCI community, I fixed my example below.

It appears that "Each context represents one or more use cases." If Context is a concept that can represent one or more use-cases, then it solves all my issues. If Context can represent more than one use-case, then Context is not precisely a use-case. In a more "nerdy" interpretation, it is a container or scope for use-cases and roles. In that container, I can put other nested contexts too, and my problem is solved.

Note that Ruby language while supporting multiple programming paradigms does not natively support DCI paradigm. I am using classes and modules to express Context, Data and Roles concepts from DCI.

Example

Below is theoretical example sophisticated enough to involve nested Contexts and to cover some of the essential points. Imagine that this is an open world game similar to "The Sims."

GameWorldContext represents application itself and has two nested Contexts (Contexts defined inside of scope of GameWorldContext) : BankContext and CoffeeShopContext . GameWorldContext is a game where "in-game human actor controlled by a player or AI" can go to a Bank and use its services or go to a Coffee Shop and drink some coffee or eat a donut. GameWorldContext has a "run" Use-Case which calls other Contexts and their use-cases and executes the application. In our game, we have only one Bank and only one CoffeeShop similarly to how a real game would have only one graphics system and only one sound system.

BankContext has two Use-Cases: deposit (deposit to a personal bank account) and invest (deposit to random investment account).

CoffeeShopContext has two Use-Cases: buy_menu_item and eat_at_the_table (eat menu item at the table).

Roles and Data definitions have Role and Data suffixes (like BankRole and BankAccountData) to make it easier to understand them in context of DCI.

Context can be a Data instance

Context can contain global to that Context Data instances (properties). For example, BankContext has personal_accounts and investment_accounts properties which are arrays of BankAccountData instances. This means that when BankContext gets created (bank = BankContext.new) it becomes Data instance too. Context as Data instance can play Roles within Use-Cases.

Conclusion

While DCI concepts like Data, Role, Context can be expressed by concepts like class, module or namespace in OOP languages(which are more class-oriented programming than OOP), pure DCI language would not require them. In pure DCI language, we can remove concepts of class, module or namespace and replace them with keywords like data, role and context. We can enforce additional constraints to prevent less experienced developers from making architectural mistakes. For example, Data definition should not include code that is an interaction between other Data instances which are not part of a state of current Data instance or using Roles in a code of Data. Similar constraints can be applied to context and role. In other words, pure DCI programming language can enforce good DCI architectural style. Another benefit of a pure DCI language would be an ability to add syntax sugar that would make programming in DCI paradigm enjoyable and efficient.