What’s in a role

Posted: February 20, 2013 in DCI, Thoughts on development
Tags: ,

On a recent blog post I received a question “Is there hope for sharing roles between contexts?” and my answer to that might at first be discouraging. In short it’s “no”

I thought it worth while to explain this a bit more since it’s a question that keeps coming back. It has surfaced on object-composition several times, often though not always it’s been due to a misconception of what a role is.

How people perceive a role is to some extend influenced by their platform of choice. In Java, where  no one has yet found a way to do pure DCI, people often take a wrapper approach and this wrapper is then what is referred to as “the role” 1. In Ruby on the other hand people often confuse roles with modules because using extend to do injection based DCI is how you would implement the role logic (at least prior to maroon).

However the concept of a role is agnostic to implementation details. It’s first and foremost an identifier. At run time the role is what identifies the current role player. It also identifies the role specific behavior for that role player. or to use Trygve Reenskaugs words “A role is an abstraction that highlights an object’s identity and external properties while it ignores the object’s internal construction.”

Usually when people talk about sharing roles between contexts they talk about sharing behavior between contexts and not sharing roles. The latter is frivolous, it’s like asking if we can share the role of Claudius the uncle of Prince Hamlet in the play by Shakespeare and Claudius in the TV-series “I, Claudius“.

What then if we rephrase the question to “Can we share implementation of behavior between contexts?”  then the answer is “yes but…”. One important goal of architecture and design is to segregate based on the rate of change and based on reasons to change. Often it will be found that two roles do not share reasons for change, and that they in fact share behavior is incidental. Sharing implementation of behavior between roles in two contexts would in that case lead to accidental complexity.

In the Canonical DCI example there’s two account objects playing each their part in a transfer context and among other capabilities they both have a balance attribute.

Let’s say that in a different context in the bank, we need to calculate the balance of a given set of accounts. Upfront it would seem that a simplistic model of an Account might suffice.  We store the balance as a decimal number and add and subtract to this number as we withdraw and deposit. And initially this would indeed suffice.
However there’s different reasons to change at play. In the second context it’s the accounting department at play and when doing analyses for their Solcency II modelling, they realize that they need the ability to exclude certain kinds of events, before using them for machine based modelling and they need to get the balance at certain days in the past as well as projected into the future. Neither of which can be handled by the original model. Between these to views of an account there are several commonalities

The account context in the above example captures some of these commonalities. It’s a context that based on a collection of ledger entries can calculate the balance. It can be used in the case where you simply need the balance of the account, it can be used if you need the balance based on only a subset of the entries. They can be filtered based on e.g. date or type. The only aspect that it can’t handle is the projection but that is usually based on the account it self and therefor the account can play a role in a ProjectedAccount context. In this respect behavior is shared between different role players in different enactment of the same context and the behavior that’s not common, is placed in a different context, where a context is playing one of the roles.
I’ve seen numerous examples of where someone would want to share roles between contexts but all examples I’ve seen so far have fallen a part when analyzing rate of change for each or reasons for change to happen. There are plenty examples of “reimplementing” behavior because though at first it seems to be the same it’s not. Just take any mathematical operator. Conceptually there’s no difference between 1/3 and 1.0/3.0.
In a mathematical perspective they are semantically identical. In most programming languages they represent different behavior. Similar is often true for addition. E.g in C# 1+x and 1l+x (where the first is 32 bit integer addition and the latter is 64-bit addition) are semantically different. For the division of integers and floating point numbers there’s different reasons for change E.g. we could get a requirement for triple precision floating point or an arbitrary precision floating point (in C# that’s decimal) integers are them self with absolute precision but the operations have limited precision in their results, so a requirement to keep the precision in the result could arise and would require changing the type of the result to something different than the operands. (This of course would violate the semantics we’ve grown accustomed to).
If such simple behavior with very strict definitions can be semantically different and have different reasons to change, odds are that more complex business logic with loose definitions share the risk of ending up being different beast even though they at first seems to be not just similar but exactly the same thing. Be mindful of the importance of not to make two similar but different concepts into the same. Share behavior where the behavior is the same, “reimplement”  where it’s not. The dijkstra examples in Ruby are a good example of this, where several roles have the same implementation of role methods, eventhough it’s technically possible to share the implementation

1) This is the self schizophrenia showing it’s ugly head an perturbating the mental model of the developers, so that when they communicate they are use the same words as the users but a referring to slightly different aspects of the application

  1. My chief struggle with DCI is knowing how to handle things that fall outside the Data, Context, Role paradigm.

    For example, let’s say I have an object for which I’m always invoking a certain method: “am_i_on?” And let’s say the method requires several lines to return a correct answer, so, it’s worth testing. Furthermore, I’m asking the object “am_i_on?” IN MULTIPLE PLACES. I invoke it in Context objects, Roles, and even in presentation views.

    In short: Where does the method belong? If not in a shared role – because I might forget what it was for originally, and begin using it for other things – then where? Is this where DCI stops, and I need to pull from another bag of OO tools?

    I suppose I could add the method to my data object, but then I’ve destroyed my favorite part of DCI: the ability to pass ANY object with the right data properties to a context and have it determine behavior at runtime.


    • runefs says:

      Your question is a common one. The short non-instructive is “It depends”. I, Jim and Trygve have tried answering this question several times on the object-composition group but since there’s no simple answer the answers often lead to even more questions or at worst the questioner giving up. I’m writing on a series of post where I try to explain DCI by example. The first example I’m using won’t shed too much light on possible solutions for this problem though but I will have to get back to at least a possible solution. Often when I find my self in a situation like the one you describe I start looking for what system the operation belongs to. If it turns out to be true that the operation (am_i_on) relates to a system then it’s often also true that use a nested context. Where the nested context represent this system. So let’s say the “am_i_on” is determining whether specific object is with in a viewable area (true) or outside (false) then that is a description of a system. Lets call the system for a viewarea system. In this viewarea system there’s two roles. The area and the object. There’s also one interaction. is_object_viewable. Anywhere where you need the “am_I_on” functionality is representing a viewarea system and you can (shuold?) therefor represent it as a viewarea system.
      What I’ve found with this approach is that I need to think differently when I’m designing. I need to work from the outside in. Starting at the user experience and work my way in through levels of systems. This is very much in line with how Alan Kay originally defined object orientation. Where he equated an object with a system made up of a network of interconnected systems. Designing systems this way I find that I don’t encounter this problem that often

  2. Yeah, an example would definitely help.

    Aside: I’ve implemented DCI across a range of projects, with much success. BUT, I still feel like I’m lost. For example: are contexts for commands only, or also for queries? Another: Do I instantiate objects in contexts, or inject them with instantiated objects? There’s a host of questions about DCI that result in: “it depends”.

    Thus far, I’ve invoked DCI with this rule: When I feel like it. How do I implement the context? However it feels good. Has it worked out? Yes, but I’m still left with a poor set of heuristics to make DCI compelling to others. It’s like we’ve found something worth keeping, but it’s not polished, nor can we articulate why it has a future.

    • runefs says:

      They are very motivating questions, it will take quite some time before I’ll be able to cover them all but you are not the first to ask them. I hope that I by giving examples will be able to answer most of them. Trygve, James and I recently had a conversation where we all agreed that every time we coded something new with DCI we had a new A-ha experience. We’ve all been asking some of your questions and the truth is that even the three of us do not entirely agree on some of them mostly on subtle points but at least one larger difference exists. I found my self asking the same questions as you do over and over again until I started not just coding using DCI but also designing using DCI. That is describing the system operations based on roles. Some of those are actually subsystems and for those subsystem the work is recursive. I use context “every where” as you will see from my examples.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s