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
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
> 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.
. I am
using what we call "private mixins" in Qi4j, which means that clients
CANNOT access the domain interface.
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
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 mailto: try...@ifi.uio.no
Morgedalsvn. 5A http://heim.ifi.uio.no/~trygver
N-0378 Oslo Tel: (+47) 22 49 57 27
Norway