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

