DCI in Java/Qi4j

305 views
Skip to first unread message

Rickard Öberg

unread,
Jan 21, 2009, 11:41:00 AM1/21/09
to object-co...@googlegroups.com
Hi guys!

After having had James at our Jayway office in Malmö yesterday to
discuss DCI, and how it could possibly be implemented using Java/Qi4j, I
have now put together a sample of the "money transfer" scenario. Or
rather, I have put together 3 variations, to show how the core
interaction, TransferMoney, can be used with 3 different domain models
*without having to change*. I have also explicitly modelled the
"context" object, which is responsible for finding the accounts and
instantiating the TransferMoney object. This was not done in any of the
other examples I looked at.

I'm not sure how much to tell you about it in this email, and what
specific things needs to be discussed about it. You can download it from
the Files section of the group, and if you have Java and Maven
installed, you can compile+run it using "mvn install".

The role implementations are declared statically at compile-time, so
there's no way to dynamically add roles to an object at runtime. I am
using what we call "private mixins" in Qi4j, which means that clients
CANNOT access the domain interface. Only the roles, which are inside the
object instance, can have access to those methods. This ensures that all
access is through roles. This is an important point when trying to
differentiate between an objects internal domain model and the roles
that it participates with.

The role interfaces MoneySource and MoneySink are declared in the
TransferMoney interaction. This means that they are very specific for
that interaction, and will probably not be reused for other
interactions. I'm not sure if this is a good way to do it, since it
might lead to an unnecessarily large amount of role interfaces for
domain objects to implement, as they are not really reusable between
interactions. Here is the definition of the TransferMoney interaction in
any case:
public class TransferMoney
implements Runnable
{
// Roles used by this interaction
public interface MoneySource
{
void transferTo(int amount, MoneySink sink)
throws IllegalArgumentException;
}

public interface MoneySink
{
void receive(int amount);
}

private MoneySource source;
private MoneySink sink;
private int amount;

public TransferMoney(MoneySource source, MoneySink sink)
{
this.source = source;
this.sink = sink;
}

public void setAmount(int amount)
{
this.amount = amount;
}

public void run()
{
// Perform the actual transfer
source.transferTo(amount, sink);
}
}
---
Like I said above, the above will be used in 3 different ways: one with
Accounts that manages the balance using an instance variable, one with
Accounts that use a transaction log for the balance, and one which uses
persistent objects that are stored in a database. The last case is
probably the most realistic one.

If you have any questions about the characteristics of this solution,
let me know and I'll explain how it works!

/Rickard

--
Rickard Öberg
Jayway
email: rickar...@jayway.se
phone: +46-70-3385304

rickard_oberg.vcf

Agata Przybyszewska

unread,
Jan 21, 2009, 2:03:53 PM1/21/09
to object-co...@googlegroups.com
Hello Richard

Is it a design choice that you don't wan't to add roles dynamically at
runtime?
My small example in java allows you to add roles dynamically.

Did you get any further with thinking about the contex?
I still feel there is a missing part - how the scopes should relate to
each other - and should it only be scoping.

Anyway, I enjoyed hanging out with you at Hindsgavl.

Agata
> begin:vcard
> fn;quoted-printable:Rickard =C3=96berg
> n;quoted-printable:=C3=96berg;Rickard
> org:Jayway
> adr;quoted-printable:;;Olaigatan 1B;=C3=96rebro;;703 61;Sweden
> email;internet:rickar...@jayway.se
> title:Software developer
> tel;work:+46-70-3385304
> tel;cell:+46-70-3385304
> x-mozilla-html:FALSE
> url:http://www.jayway.se
> version:2.1
> end:vcard
>

_____________________________________
Agata Przybyszewska, Ph.D.

Mathmagicians
Stjerneskibet, Havnegade 29
DK-5000 Odense C

Tlf +45 51880669
Mail ag...@mathmagicians.dk
Web www.mathmagicians.dk

Rickard Öberg

unread,
Jan 21, 2009, 2:14:04 PM1/21/09
to object-co...@googlegroups.com
Agata Przybyszewska wrote:
> Hello Richard
>
> Is it a design choice that you don't wan't to add roles dynamically at
> runtime?
> My small example in java allows you to add roles dynamically.

It is definitely a design choice. We had dynamic composition in Qi4j
when we started, but quickly decided that it would be too chaotic, and
too difficult to reason about. Another issue is that if we do it
statically then we can generate metadata about our model, and do
visualization or documentation from it. With a dynamic runtime model
that is impossible.

> Did you get any further with thinking about the contex?
> I still feel there is a missing part - how the scopes should relate to
> each other - and should it only be scoping.

Not sure exactly what you are referring to here. Can you be more specific?

/Rickard

Agata Przybyszewska

unread,
Jan 21, 2009, 2:37:07 PM1/21/09
to object-co...@googlegroups.com
I am still having some conceptual problems with understanding the role of context - or at least there is a weak part in MY understanding.
We model the domain, have data in objects, and have use cases for interactions.
The context is connecting roles with usecases, and associating roles with objects.

Lets say we have a use case, in which objects o_1, .. o_n participate, each with a couple of roles r_{1,1}, r_{1,2} , ... r_{n,i_n}.
Should we model scope in the context object?
Should there be progress and state in the usecase - some kind of FSM?
Should we have some sort of dependency graph (like Eclipses OSGI does - which in my eyes is a Context on the service level)?
Should we model requirements into the usecase directly?
What about inter-usecase interoperability?

I think the implementations of the MoneyTransfer example this far have only shown a trivial context - like a bag where you can throw stuff in - like say a Session variable. It is probably not the fault of the implementations, but of the simplicity of the example. To me, the devil is always in the details.

I am working on finishing my Scala implementation of a small context-data-interaction framework, but I am not entirely satisfied with the "bag/session" aproach to context.

Agata


Rickard Öberg skrev:

James O. Coplien

unread,
Jan 21, 2009, 2:48:20 PM1/21/09
to object-co...@googlegroups.com

On Jan 21, 2009, at 8:37 , Agata Przybyszewska wrote:

> Lets say we have a use case, in which objects o_1, .. o_n
> participate, each with a couple of roles r_{1,1}, r_{1,2} , ...
> r_{n,i_n}.
> Should we model scope in the context object?

DCI models end users' cognitive models. I don't think scope is part of
that model. I am not sure what you mean.


> Should there be progress and state in the usecase - some kind of FSM?

There is progress and state in the use case. I am not sure why an FSM
is relevant. FSMs in interfaces are modal, and modal interfaces tend
to lead to errors.


> Should we have some sort of dependency graph (like Eclipses OSGI
> does - which in my eyes is a Context on the service level)?


You should download and run Trygve's example (the one I showed you at
JaOO) in which you use such a graph to program the roles and to show
their collaboration in a given context (in the interaction view). I
think that everything necessary is present in the Interaction view and
that, in fact, the generated code (the traits) in the Context view can
go away.


> Should we model requirements into the usecase directly?


In the classical definition of a Use Case, a Use Case captures more
than the scenarios, though it is typically a collection of potential
related scenarios between an end user and a program under construction
(Alistair Cockburn's definition; the UML definition is somewhat
different.) Use Cases say "what," and need to be translated into
algorithms that say "how." DCI was once called "DCA," with the "A"
standing for "Algorithm." Now they are called interactions. (Trygve
and I nailed down a few more vocabulary terms in our meeting a couple
of weeks ago, and we probably need an updated glossary.)

Given those terms: can you tell me how I would recognize that I had
answered your question, if I felt that I had?


> What about inter-usecase interoperability?

My account example shows how one algorithm "invokes" another. There
are more general ideas here that come from the object generalizations
in the DCI framework. Technically, a SavingsAccount is not a domain
object in a real bank: the real objects are transaction logs, database
audits, etc. However, a SavingsAccount can be thought of as a
collaboration between users, transaction logs and audit trails. Its
"methods" can be invoked by the higher-order "methods" such as that of
paying all bills. In general, it can be turtles all the way down.

James O. Coplien

unread,
Jan 21, 2009, 3:49:22 PM1/21/09
to object-co...@googlegroups.com

On Jan 21, 2009, at 5:41 , Rickard Öberg wrote:

. I am 
using what we call "private mixins" in Qi4j, which means that clients 
CANNOT access the domain interface. 

... just to be careful to point out that clients *can* (indirectly) access these interfaces if the role chooses to publish them. And that's how it should be.

I have taken the additional step that the private mixins may access the domain class methods only through an ancillary role called "this". That provides a clean separation between the role logic and the domain logic. That is, roles should have no special privileges in accessing class member functions any more than any other outsider.

(Steen -- the Ruby example could actually be sharpened up in this regard as well. It current says:

def transfer_out
    raise "Insufficient funds" if balance < context.amount  
    withdraw context.amount
    context.destination_account.deposit context.amount
    update_log "Transfer Out", Time.now, context.amount
    context.destination_account.update_log "Transfer In", Time.now, context.amount
  end

and maybe should say something like:

def transfer_out
    raise "Insufficient funds" if balance < context.amount  
    context.self.withdraw context.amount
    context.destination_account.deposit context.amount
    context.self.update_log "Transfer Out", Time.now, context.amount
    context.destination_account.update_log "Transfer In", Time.now, context.amount
  end

An easy fix, methinks :-)   )

Rickard Öberg

unread,
Jan 22, 2009, 3:04:47 AM1/22/09
to object-co...@googlegroups.com
James O. Coplien wrote:
> I have taken the additional step that the private mixins may access the
> domain class methods only through an ancillary role called "this". That
> provides a clean separation between the role logic and the domain logic.
> That is, roles should have no special privileges in accessing class
> member functions any more than any other outsider.
>
> (Steen -- the Ruby example could actually be sharpened up in this regard
> as well. It current says:
>
> def transfer_out
> raise "Insufficient funds" if balance < context.amount
> withdraw context.amount
> context.destination_account.deposit context.amount
> update_log "Transfer Out", Time.now, context.amount
> context.destination_account.update_log "Transfer In", Time.now,
> context.amount
> end

Ok, so here's a question: in the above the amount is part of the
context. This makes the TransferMoney interaction essentially stateless.
However, in my own example, the context is only responsible for
acquiring the objects that play the roles. Once the context has done
this and created the interaction object TransferMoney, the amount is set
on TransferMoney, which can then proceed.

The idea with that approach is that the context needs to fulfill the
contract of the TransferMoney interaction, that is, the context needs to
supply two instances fulfilling the roles required by the interaction.
But there is no contract from TransferMoney to the context (i.e. the
context does not need to declare that it exposes the amount), so there
is no dependency in that direction. That seems to make more sense to me,
as there can be many context implementations, but only one interaction.

/Rickard

Steen Lehmann

unread,
Jan 22, 2009, 3:10:17 AM1/22/09
to object-co...@googlegroups.com
2009/1/21 James O. Coplien <jcop...@gmail.com>:
[...]

Could be, I'm just not getting the new example. "context.self" in Ruby
would refer to the context object and is the same as "context". We
could use "context.this" to mean something else, at the risk of
confusing people. However, "withdraw" is being invoked on the domain
object, so how does the context map "this" to a specific domain
object, since there can be several domain objects in use for a given
context? Maybe you can elaborate a bit.

-- Steen

Trygve Reenskaug

unread,
Jan 22, 2009, 4:32:27 AM1/22/09
to object-co...@googlegroups.com
Hello Agata,
Your mail made me think. Why don't we be add roles dynamically at runtime? A role is a name I, as a programmer, give to objects that serve a certain purpose at runtime. A context is an object that binds role names to objects at runtime. The context object is often visible to the interaction methods (role methods). I have tried to let role methods add new roles to the context. Easy. But then I cannot use these names in my code because they are not known at compile time??? There is a contradiction here. I, as a programmer, know theses role because I write code that adds them at runtime. Yet, I am not allowed to know them when I write code! What could I gain by pretending not to know them? If I know the name, then I can define it statically in the context class.

My context knows all roles. They are used in some executions, not used in other. The key is that the binding is done dynamically and can be done at any time. So I find no use for adding roles at runtime. I do find a use for ordering the context to find an object and bind it to a certain role at a certain time. Possibly because it is only at this stage in the execution that I discover the need to access this object and it is only at this stage I know enough to be able to select it.

So for a short answer.  It is not a design choice that I don't want to add roles dynamically at run time. I don't do it simply because it will not give me anything I cannot do better with compile time roles.

Hope this clarifies my views on your question. Keep up your good work!
--Trygve
--

Trygve Reenskaug       mailto: try...@ifi.uio.no

Morgedalsvn. 5A         http://heim.ifi.uio.no/~trygver

N-0378 Oslo               Tel: (+47) 22 49 57 27

Norway

Reply all
Reply to author
Forward
0 new messages