DCI sample not dci?

34 views
Skip to first unread message

Marc Grue

unread,
Oct 7, 2011, 10:31:16 AM10/7/11
to dci-evolution
On 7 Okt, 11:21, "James O. Coplien" <jcopl...@gmail.com> wrote:
> Here on the DCI-evolution group, I want to stick to sound concepts and how to evolve them into something powerful. I hope we will not limit them with the poorly conceived architectures of current frameworks.

Guilty :) I have been unclear. I'll try not to get on deep water too
much. So I'll try to be more concrete which I think could be more
productive.

Just to confirm: I understood that people here wanted more complex
near-real-world examples, and that the DDD sample was mentioned as a
good example to port? I did a version a and b port to what I have so
far understood as DCI (based on my erlier MoneyTransfer and
FrontLoading examples), and I'm keen to understand how it can be made
as DCI compliant as possible. So I don't see it as a conclusion but a
platform for further exploration of how to code a complex domain
according to sound DCI principles which I understand that James is
questioning:

> The more I look at the DDD port the more I see considerable compromises to DCI principles.

Do you mean in the code - can you be more specific, possibly take out
an example?

> I am concerned that people will take that port as being what DCI is. It is not.

Can you help me understand what you find is not DCI in the code? I
would sincerely like to learn how that is and work on a version c that
would be more DCI compliant.

> The DDD architecture [I presume my DCI Sample?] reflects pretty static thinking; it says a lot about static things but little about objects.

Example in code?

Cheers,
Marc

Ant Kutschera

unread,
Oct 7, 2011, 5:07:53 PM10/7/11
to dci-evolution
Hi Marc,

I have some questions / comments:

1) What's the rolemap package for? Is it really necessary? I don't
like how it is outside the actual contexts, but refers to role
classes.

2) Finding the actual DCI stuff is tricky, because there is a ton of
(qi4j or wicket?) boilerplate hanging around.

3) In the BookNewCargo context, you assign the role BookingSystem to a
CargoFactory. CargoFactory seems not to be a simple object containing
state. Rather it seems to be pure behaviour. Why is it playing the
role BookingSystem?

4) I thought qi4j let you do DCI out of the box, but you have a few
classes in the project in a dci package. What does the manual say
about DCI, and will those classes be added to qi4j?

5) I like how you can inject services into roles. My frameworks lets
you do similar things with all kinds of resources.

6) In the booking system role, you have a field annotated with "This"
- is that "self" - why not call the field "self" rather than
"cargoFactory"?

7) Lines like this don't help people figure out where the DCI in the
example is:


delivery.prototype().routingStatus().set( RoutingStatus.NOT_ROUTED )

Why not just: delivery.setRoutingStatus(RoutingStatus.NOT_ROUTED)

8) Some might criticise the BookNewCargo context, because there is no
interaction. I can live with it - you are injecting behaviour into an
object - thats what contexts do.

9) DCI is about readable code. The following makes me puke:

transportStatus = cargo.delivery().get().transportStatus().get();

"get"? I know, it's qi4j, and it's giving you other benefits (what?),
but still.

10) RegisterNewDestination context:

new InspectCargoDeliveryStatus( cargo ).update();

Starting a sub-context - good example. Is the InspectCargoDelivery

11) I like how you have added comments for all use-case steps to the
code. It really highlights how DCI is use-case (or collaboration)
centric. Same is true for the excellent documentation for each
context - makes it clear what is supposed to be going on.

12) ParseHandlingEventData "context" - it's a service... And contains
use-case code... So SOA *can* do it too - like I've always said :-)

14) The annotation @NotEmpty - JSR-303 bean validation should make
that deprecated. Hibernate finally have a final release out - its the
reference impl.

15) One reason for creating interfaces to services (and perhaps roles?
hmmm food for thought) is so that you can choose the implementation at
deployment time. If ParseHandlingEventData really is a service, move
the impl out of the file containing the interface.

You look to have spent a lot of time on this - good effort. Don't
take my comments too negatively.

I wonder if James is right, that while this is a good example because
it is "production ready", it is over complex? How much of the code is
really about DCI? Perhaps a third? And to read that, you need to get
over things like qi4j (its odd syntax and all the validation it
delivers for "free"), wicket, etc. It will be a hard tradeoff to
produce real world examples where the reader can't get lost in the
technical stuff. I guess in terms of DCI evolution, one would want
none of the technical crap, because the point is, the code is meant to
be readable.

I'll see what I can do over the coming weeks, whether I can do the
same implementation with my framework, and cut out some of the techy
stuff.

One question: did the use-cases come with the DDD implementation? Or
did you create them? And how did you identify the roles and their
players?

Cheers,
Ant

Rickard Öberg

unread,
Oct 7, 2011, 9:54:50 PM10/7/11
to dci-ev...@googlegroups.com
On 10/8/11 05:07 , Ant Kutschera wrote:
> 1) What's the rolemap package for? Is it really necessary? I don't
> like how it is outside the actual contexts, but refers to role
> classes.

This is one of the questions I asked as well. I'm very torn about it. On
the one hand it is nice to have one place to find out what entities
participate in what use cases, but there is also the leakage of
Context-internal concepts.

> 4) I thought qi4j let you do DCI out of the box, but you have a few
> classes in the project in a dci package. What does the manual say
> about DCI, and will those classes be added to qi4j?

We are adding some DCI stuff to Qi4j 2.0, but that is not yet available
in a release. We are also still experimenting, I would say, with what is
the recommended way to do it.

> 7) Lines like this don't help people figure out where the DCI in the
> example is:
>
> delivery.prototype().routingStatus().set( RoutingStatus.NOT_ROUTED )
>
> Why not just: delivery.setRoutingStatus(RoutingStatus.NOT_ROUTED)

In Qi4j properties and associations are first-class things. Cuts down on
boilerplate, and makes it easier to apply constraints, but the syntax is
a bit funky with the get()/set() methods. It's mostly a matter of
getting used to it IMO.

> 9) DCI is about readable code. The following makes me puke:
>
> transportStatus = cargo.delivery().get().transportStatus().get();
>
> "get"? I know, it's qi4j, and it's giving you other benefits (what?),
> but still.

See above. Being able to apply constraints to properties in a
well-defined place is one benefit, another is that you can easily
reference the property in queries to the indexes using our Query DSL.

> 14) The annotation @NotEmpty - JSR-303 bean validation should make
> that deprecated. Hibernate finally have a final release out - its the
> reference impl.

My hope is that Qi4j v2.0 will implement JSR-303, and not just for
properties but also for method parameters and return values. I hope
people will stop using Hibernate eventually, because storing objects in
a database is basically stupid. I use relational databases for reporting
purposes, and that is about it. Even that I find to be non-trivial
actully. It depends on the use case.

> 15) One reason for creating interfaces to services (and perhaps roles?
> hmmm food for thought) is so that you can choose the implementation at
> deployment time. If ParseHandlingEventData really is a service, move
> the impl out of the file containing the interface.

In Qi4j 1.x you had to use interfaces if you wanted to use constraints
and concerns. In Qi4j 2.x it works with plain objects as well, so I
imagine a lot of services will not use interfaces anymore.

/Rickard

rune funch

unread,
Oct 8, 2011, 2:59:27 AM10/8/11
to dci-ev...@googlegroups.com
Den 07/10/2011 kl. 23.07 skrev Ant Kutschera <ant.ku...@gmail.com>:

> I can live with it - you are injecting behaviour into an
> object - thats what contexts do

That's what injecting behavior do. If there's no need for at context
why have one?

Ant Kutschera

unread,
Oct 8, 2011, 4:26:01 AM10/8/11
to dci-evolution
On Oct 8, 8:59 am, rune funch <funchsolt...@gmail.com> wrote:
> Den 07/10/2011 kl. 23.07 skrev Ant Kutschera <ant.kutsch...@gmail.com>:
>
> > I can live with it - you are injecting behaviour into an
> > object - thats what contexts do
>
> That's what injecting behavior do. If there's no need for at context
> why have one?

Because so far, DCI has always been about injecting behaviour within a
context.

If you want to propose injecting behaviour in any place, that could be
interesting too... what do others think?

rune funch

unread,
Oct 8, 2011, 4:40:33 AM10/8/11
to dci-ev...@googlegroups.com
Den 08/10/2011 kl. 10.26 skrev Ant Kutschera <ant.ku...@gmail.com>:

> On Oct 8, 8:59 am, rune funch <funchsolt...@gmail.com> wrote:
>> Den 07/10/2011 kl. 23.07 skrev Ant Kutschera <ant.kutsch...@gmail.com>:
>>
>>> I can live with it - you are injecting behaviour into an
>>> object - thats what contexts do
>>
>> That's what injecting behavior do. If there's no need for at context
>> why have one?
>
> Because so far, DCI has always been about injecting behaviour within a
> context.

It's a feature of a context to inject behavior to me it's not it's
purpose. Encapsulation is a feature of an object that doesn't make it
the motivation for using objects in the first place


> If you want to propose injecting behaviour in any place, that could be
> interesting too... what do others think?

Never said anything like that

James O. Coplien

unread,
Oct 8, 2011, 4:55:05 AM10/8/11
to dci-ev...@googlegroups.com

On Oct 8, 2011, at 10:26 , Ant Kutschera wrote:

> If you want to propose injecting behaviour in any place, that could be interesting too... what do others think?


Trygve and I decided that all injection should be done in a rebind method, so that there is a single, atomic, action that guarantees the integrity and consistency of the result. It also means that the playbill is completely filled out at the beginning of the play, which lends to static analysis. Together, those two are much of the foundation of being able to understand the run-time behavior by looking just at the compile-time source code.

Let's say that you conditionally bound a role to some object in the middle of execute(). Until then, you have an unbound role sitting around. How can you evaluate, from the source code, whether that role is ever used in an unbound state?

Worse yet, let's say that you created roles on the fly. It makes it impossible to reason about the code.

We have lots of ways of thinking about reflection, dependency injection, and other tricks. They have become popular only in very narrow areas. I posit that they have taken off only where their presence doesn't detract from code comprehension. DCI encapsulates these bindings in a way that makes the static code more accessible and more indicative of run-time behavior than if I allow arbitrary dependency injection, or arbitrary mix-ins. Read the Elephant paper.

ant.ku...@gmail.com

unread,
Oct 8, 2011, 6:39:00 AM10/8/11
to dci-ev...@googlegroups.com
We have a keeper: role binding only occurs in the rebind method of a context.

One question... Rebind *method*? Or could it occur in the context constructor?  Or does a context have a constructor and rebind method like a class has a constructor (or analogous place) used to instantiate objects?  Or is it too early to commit to that?

James O. Coplien

unread,
Oct 8, 2011, 6:45:11 AM10/8/11
to dci-ev...@googlegroups.com

On Oct 8, 2011, at 12:39 , ant.ku...@gmail.com wrote:

We have a keeper: role binding only occurs in the rebind method of a context.

One question... Rebind *method*? Or could it occur in the context constructor?  Or does a context have a constructor and rebind method like a class has a constructor (or analogous place) used to instantiate objects?  Or is it too early to commit to that?


I think these are all possibilities, and I don't think it makes a big difference in general. The details become important if you are doing iteration / recursion, because then the rebind method can be called multiple times during any single Context instantiation.

I personally prefer to instead recur on Context instantiations, but that's just a matter of personal programming style and preference.

rune funch

unread,
Oct 8, 2011, 7:02:33 AM10/8/11
to dci-ev...@googlegroups.com
Den 08/10/2011 kl. 12.45 skrev "James O. Coplien" <jcop...@gmail.com>:

> I personally prefer to instead recur on Context instantiations, but that's just a matter of personal programming style and preference

I agree and looking back on last time we had the same debate that
seemed to have been the case then too. Some prefer rebinding in the
same context instance, some prefer to instantiate a new context and
only bind when instantiating. I'm personally in the latter group but
don't see anything problematic with rebinding in an instance.
My personal preference is due to my model of a context. As an analogy:
If I was the director of a theater and that theater played Romeo and
Juliet every night for a month but with a new cast every night would I
say that it was the same play or similar plays? The (user) audience
(might) have an opinion on that. If one night the cast decided to do
the movie interpretation from the '90s it would still be the same
script (same context identity) but the experience of it would be
different (different context identity). I don't think both views are
valid from each of there own perspective

-Rune

Marc Grue

unread,
Oct 8, 2011, 8:03:14 AM10/8/11
to dci-ev...@googlegroups.com
Hi Ant,

Great questions and comments! I'm just happy to dive in:

On 2011-10-07, at 23.07, Ant Kutschera wrote:
Hi Marc,

I have some questions / comments:

1) What's the rolemap package for?  Is it really necessary? I don't
like how it is outside the actual contexts, but refers to role
classes.

The rolemap package in this implementation holds the mapping between an entity interface (becoming a Data object later) and the Roles it can play in various Contexts. The rolemap doesn't have any influence on what Role a Data object is going to play or when. This is entirely the responsitbility of the Context to decide. So if a Context needs a certain Data object to play a Role, the programmer assign that capability to the Data object by adding the Role to the RoleMap for that Data object. The RoleMap has no knowledge of or affect to the use case processed in the Context as far as I can see. So I'm not sure if you can call it "leakage of Context-internal concepts" as Rickard mentioned?

2) Finding the actual DCI stuff is tricky, because there is a ton of
(qi4j or wicket?) boilerplate hanging around.

My communication layer is all Wicket and very little Qi4j (just for instantiation some Qi4j service/transienComposites). Wicket is based on a lot of callbacks that can make it look pretty convoluted. Any other presentation technology (jsp, whatever) could have been used here. I wanted to use Wicket since I like the separation between "presentation logic" (misrouted shows in red etc) and html/css.

I have seen the DCI part as being applied to the Context and Data layers only - but there might be a bigger picture to consider (users mental model...)? So to me, I would look for the core DCI part in the Contexts in the context.interaction package. That's where the use cases are processed.

3) In the BookNewCargo context, you assign the role BookingSystem to a
CargoFactory.  CargoFactory seems not to be a simple object containing
state.  Rather it seems to be pure behaviour.  Why is it playing the
role BookingSystem?

I have been in dought about this one. You're right, the CargoFactory has no state and is probably weird to think of as a "Data object" - well it's not. I have seen the CargoFactory as involved in a "CargoAggregateRoot" which could have had state in the form of an association to cargos. The same goes for HandlingEventAggregateRoot playing a EventRegistrarRole in RegisterHandlingEvent. The aggregate root stuff is still puzzling me, but I tried to lean at Udi Dahans thoughts about this in


... although I might not have implemented/understood it correctly. I also tried to copy some of the aggregate approach of Rickards Streamflow project (correct me if I'm wrong, Rickard - thinking of the OrganizationsEntity and UsersEntity). Input is welcome!

Since booking a new cargo is such a central use case of the application, I decided to have a Context implement it rather than a "Context service" which could be an alternative. There is use case specific knowledge in the BookNewCargo Context about the earliestDeparture date (that it's today) and the various delivery statusses that I wouldn't like to have a service knowing about... 

4) I thought qi4j let you do DCI out of the box, but you have a few
classes in the project in a dci package.  What does the manual say
about DCI, and will those classes be added to qi4j?

These are just my convenience classes for boilerplate stuff to instantiate the Data objects with an id for instance. I have a rather clumsy manual Context "injection" (based on reflection) that I would like to inject more smoothly. Rickard has a nice way of doing this with a Qi4j annotation but I didn't use that in version a since it caused some problems when using ValueComposites as Role Players. But since I don't use those in version b, I could go back to that solution again, actually - will look at this for version c!

5) I like how you can inject services into roles.  My frameworks lets
you do similar things with all kinds of resources.

Great!

6) In the booking system role, you have a field annotated with "This"
- is that "self" - why not call the field "self" rather than
"cargoFactory"?

I might as well call it this:

@This
CargoFactory self;

and then say 

self.createCargo(...)

Since my Data objects are Qi4j composites, they could consist of several Data structures (in this version none does though), so we could have had

@This
Cargo cargo;

@This
CargoOwner cargoOwner;

etc...

If you used "self" only in this case, then you would have to cast self to either Cargo or CargoOwner in order to access the data of the two. I guess its a matter of how you view a composite. As one object that you can cast to any leaf or as individual leaves...

7) Lines like this don't help people figure out where the DCI in the
example is:

delivery.prototype().routingStatus().set( RoutingStatus.NOT_ROUTED )

Why not just: delivery.setRoutingStatus(RoutingStatus.NOT_ROUTED)

You can do that too, as I did in line 112-116 of InspectReceivedCargo. The Qi4j prototype stuff there is in the beginning once instead of in each assignment line as you saw. That's better, I agree. But you get used to it as Rickard mentioned and it no longer looks messy :-)

8) Some might criticise the BookNewCargo context, because there is no
interaction.  I can live with it - you are injecting behaviour into an
object - thats what contexts do.

I agree, there's not much happening there, but still there's use case knowledge (next expected handling event is RECEIVE etc).

You touch a more general DCI execution model question I have had with version b. Practically all my contexts have only one methodful Role and some methodless roles (Context fields with values). Is it "normal"/"within dci" to have only one Role implementation in a Context?


9) DCI is about readable code.  The following makes me puke:

   transportStatus = cargo.delivery().get().transportStatus().get();

"get"?  I know, it's qi4j, and it's giving you other benefits (what?),
but still.

See my comments to 7) and Rickards comments too.

10) RegisterNewDestination context:

               new InspectCargoDeliveryStatus( cargo ).update();

Starting a sub-context - good example.  Is the InspectCargoDelivery

(end of sentence missing?)

11) I like how you have added comments for all use-case steps to the
code.  It really highlights how DCI is use-case (or collaboration)
centric.  Same is true for the excellent documentation for each
context - makes it clear what is supposed to be going on.

Thanks! Good to hear it helps. :-)

12) ParseHandlingEventData "context" - it's a service...  And contains
use-case code...  So SOA *can* do it too - like I've always said :-)

I agree, it could definitely be a service too! I chose to implement it as a Context for some of the same reasons as described in my comments to 3). That it implements a use case rather than being a more "neutral" service used by Contexts. It's also step 1 of the bigger summary use case ProcessHandlingEvent - so conceptually I found it more in the "Context department" than in the "Service department". I don't know if this violates some dci principles or is a matter of personal style?

14) The annotation @NotEmpty - JSR-303 bean validation should make
that deprecated.  Hibernate finally have a final release out - its the
reference impl.

(See Rickards comments in this thread)

15) One reason for creating interfaces to services (and perhaps roles?
hmmm food for thought) is so that you can choose the implementation at
deployment time.  If ParseHandlingEventData really is a service, move
the impl out of the file containing the interface.

(See Rickards comments in this thread)
I imagine (I don't have much experience) that you would 90% of the time use the one service implementation that you start out with. If you hit the 10% case then you could assemble another implementation (inject another mixin).

You look to have spent a lot of time on this - good effort.  Don't
take my comments too negatively.

Thanks, no no, I'm really happy that you took your time to dive into the code! :-)

I wonder if James is right, that while this is a good example because
it is "production ready", it is over complex?  How much of the code is
really about DCI?  Perhaps a third?  

I agree! I don't see the communication layer for instance as part of dci. But I wanted to tie everything together to make a fully working application that you can use "live" in a browser as though it was from a website of the shipping company. That also takes the relationship with the presentation layer into the bigger picture...

And to read that, you need to get
over things like qi4j (its odd syntax and all the validation it
delivers for "free"), wicket, etc.  It will be a hard tradeoff to
produce real world examples where the reader can't get lost in the
technical stuff.  I guess in terms of DCI evolution, one would want
none of the technical crap, because the point is, the code is meant to
be readable.

(see above)

I'll see what I can do over the coming weeks, whether I can do the
same implementation with my framework, and cut out some of the techy
stuff.

That would be awesome! If you can re-use any of my code, please go ahead! (maybe the communication layer parts?)

One question: did the use-cases come with the DDD implementation?  

Oooohhhh no! That was actually one of biggest challenges. You can see how I struggled to understand what the original DDD application was trying to do for instance in my BuildDeliverySnapshot use case (in version a) - a big mess, before I split it up into all the different handling event use cases in version b.

Or
did you create them?   And how did you identify the roles and their
players?

I simply tried to figure out what the use case steps was and who (the Role) would do that in a real shipping company. You can for sure discuss the naming of the Roles, and it would be nice to take this with a shipping domain expert! You can compare how the use cases and Roles develop from version a to b! 

Cheers,
Marc
Reply all
Reply to author
Forward
0 new messages