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
> 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?
> 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
> 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.
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 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
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?