Would it be sensible to have a @Computed on the ManyAssoc on the WorldZone ?
Which automatically does the query thing with the this instance?

Michael

Am 06.05.2009 3:42 Uhr, schrieb Rickard Öberg:
Hey Staz,

GREAT question, and one that I had to deal with a week ago. I changed
the topic to make it easier to find this later in search engines.

Staz . wrote:
The description of a problem is this:
I have two composites, WorldZone and WorldObject. The instances of the
WorldZone, may contain many instances of WorldObject. In UML, that
would be aggregation association between class WorldZone and class
WorldObject. This would also be a two-way association, in a sense that
WorldZone knows which WorldObjects it contains, and the WorldObject
knows to which WorldZone it belongs.
<snip>

There are many things to consider here. I will start with Eric's
assertion in the DDD book(p83):
"In real life, there are lots of many-to-many associations, and a great
number are naturally bidirectional. The same tends to be true of early
forms of a model as we brainstorm and explore the domain. But these
general associations complicate implementations and maintenance.
Furthermore, they communicate very little about the nature of the
relationship.

There are at least three ways of making associations more tractable.
1. Imposing a traversal direction
2. Adding a qualifier, effectively reducing multiplicity
3. Eliminating nonessential associations

It is important to constrain relationships as much as possible. A
bidrectional association means that both objects can be understood only
together. When application requirements do not call for traversal in
both directions, adding a traversal direction reduces interdependence
and simplifies the design. Understanding the domain may reveal a natural
directional bias."

So while you may think of the association as bidirectional, it may not
be that in your actual model.

Then there's another more practical side to it. If your WorldZone keeps
a ManyAssociation to the objects it contains, then anytime an object is
added or removed there will be contention and use of that
ManyAssociation. If you have a multi-user system, I would guess that not
much is going to go in or out of those zones, because you will have
ConcurrentModificationExceptions pretty much all the time due to
multiple concurrent usecases trying to change the ManyAssociation. You
also severely limit your ability to scale the system since with a
bidirectional association you HAVE to change both sides within the same
transaction. In practice this means that you have to put them on the
same server, or else you will have 2PC transactions going on, which
won't scale. Are you sure you want to do this?

All of this makes it bad to have bidirectional associations. Fortunately
there's a very simple solution to it which will give you simple code and
a scalable system: keep the Association from WorldObject to WorldZone,
and then implement the reverse as a Query. This is what I did in my
case, but my domain objects were Tasks in an Inbox (I'm modeling
workflow). In my first version I had a ManyAssociation from Inbox to
Task, but that won't scale due to the above problems with many trying to
access the ManyAssociation concurrently. So instead I changed so that a
Task has an Owner, and then an Inbox listing became this Query:
// Find all Active tasks with specific owner
QueryBuilder<SharedTaskEntity> queryBuilder =
uow.queryBuilderFactory().newQueryBuilder(SharedTaskEntity.class);

// Match the owner
Property<String> id =
templateFor(OwnableTask.OwnableTaskState.class).owner().get().identity();

// Where-expressions
queryBuilder.where(and(
QueryExpressions.eq(id, ownerId.identity()),
eq(templateFor(TaskStatus.TaskStatusState.class).status(),
Status.ACTIVE)));

// Define order of result
Query<SharedTaskEntity> inboxQuery = queryBuilder.newQuery();
inboxQuery.orderBy(orderBy(templateFor(CreatedOn.CreatedOnState.class).createdOn()));

---
And that's it. This is very scalable, because there's no contention on
anything: to have a task enter the inbox I just set the Owner to the
Inbox. Any number of threads can therefore "add" tasks to the same inbox
simultaneously, and I'll never get concurrency problems. Also, I don't
have to have the owner and task on the same box, since all I need from
the owner is the identity. The Inbox does not even have to know that
Tasks are referring to it this way! This also ensures that the result is
read-only, as there is NO way for the user to modify these results. It
also deals with massive amounts of tasks better, since the query has
startresult/maxresult settings for paging, and I'm guessing you will
need this too in your situation.

This answers how to deal with bidirectional associations.

/Rickard

_______________________________________________
qi4j-dev mailing list
[email protected]
http://lists.ops4j.org/mailman/listinfo/qi4j-dev


_______________________________________________
qi4j-dev mailing list
[email protected]
http://lists.ops4j.org/mailman/listinfo/qi4j-dev

Reply via email to