2009/5/6 Rickard Öberg <[email protected]>

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

+1 ... I did something similar (one-way assoc + reverse query) for a web-app
a few years ago, made a lot of difference

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

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

Reply via email to