Chapter 15. Bidirectional Associations

Table of Contents

1. Inverse Reference Properties
2. Making an Association-Free Information Design Model
2.1. The basic procedure
2.2. How to eliminate unidirectional associations
2.3. How to eliminate bidirectional associations
2.4. The resulting association-free design model
3. Quiz Questions
3.1. Question 1: Table populations of bidirectional associations
3.2. Question 2: Implications of bi-directionality
3.3. Question 3: Elimination of bi-directional associations
3.4. Question 4: Meaning of bi-directional association

A bidirectional association is an association that is represented as a pair of mutually inverse reference properties.

The model shown in Figure 15.1 below (about publishers, books and their authors) serves as our running example in all other parts of the tutorial. Notice that it contains two bidirectional associations, as indicated by the ownership dots at both association ends.

Figure 15.1. The Publisher-Book-Author information design model with two bidirectional associations

The Publisher-Book-Author information design model with two bidirectional associations

1. Inverse Reference Properties

For being able to easily retrieve the committees that are chaired or co-chaired by a club member, we add two reference properties to our Committee-ClubMember example model: the property of a club member to chair a committee (ClubMember::chairedCommittee) and the property of a club member to co-chair a committee (ClubMember::coChairedCommittee). We assume that any club member may chair or co-chair at most one committee (where the disjunction is non-exclusive). So, we get the following model:

Notice that there is a close correspondence between the two reference properties Committee::chair and ClubMember::chairedCommittee. They are the inverse of each other: when the club member Tom is the chair of the budget committee, expressed by the tuple ("budget committee", "Tom"), then the budget committee is the committee chaired by the club member Tom, expressed by the inverse tuple ("Tom", "budget committee"). For expressing this inverse correspondence in the diagram, we append an inverse property constraint, inverse of chair, in curly braces to the declaration of the property ClubMember::chairedCommittee, and a similar one to the property Committee::chair, as shown in the following diagram:

We also call Committee::chair and ClubMember::chairedCommittee a pair of mutually inverse reference properties. Having such a pair in a model means having redundancy because each of the two involved reference properties can be derived from the other by inversion. This type of redundancy implies data storage overhead and update overhead, which is the price to pay for the bidirectional navigability that supports efficient object access in both directions.

For maintaining the duplicate information of a mutually inverse reference property pair, it is good practice to treat one of the two involved properties as the master, and the other one as the slave, and take this distinction into consideration in the code of the change methods (such as the property setters) of the affected model classes. We indicate the slave of an inverse reference property pair in a model diagram by declaring the slave property to be a derived property using the UML notation of a slash as a prefix of the property name as shown in the following diagram:

The property chairedCommittee in ClubMember now has a slash-prefix (/) indicating that it is derived. The {inverse of chair} annotation in this example defines the derivation rule: the property is derived by inversion of Committee::chair. This implies that, the other way around, Committee::chair is the inverse of ClubMember::chairedCommittee.

In a UML class diagram, the derivation of a property can be specified, for instance, by an Object Constraint Language (OCL) expression that evaluates to the value of the derived property for the given object. In the case of a property being the inverse of another property, specified by the constraint expression {inverse of anotherProperty} appended to the property declaration, the derivation expression is implied. In our example, it evaluates to the committee object reference c such that c.chair = this.

There are two ways how to realize the derivation of a property: it may be derived on read via a read-time computation of its value, or it may be derived on update via an update-time computation performed whenever one of the variables in the derivation expression (typically, another property) changes its value. The latter case corresponds to a materialized view in an SQL database. While a reference property that is derived on read may not guarantee efficient navigation, because the on-read computation may create unacceptable latencies, a reference property that is derived on update does provide efficient navigation.

In the case of a derived reference property, the derivation expresses life cycle dependencies. These dependencies require special consideration in the code of the affected model classes by providing a number of change management mechanisms based on the functionality type of the represented association (either one-to-one, many-to-one or many-to-many).

In our example of the derived inverse reference property ClubMember::chairedCommittee, which is single-valued and optional, this means that

  1. whenever a new committee object is created (with a mandatory chair assignment), the corresponding ClubMember::chairedCommittee property has to be assigned accordingly;

  2. whenever the chair property is updated (that is, a new chair is assigned to a committee), the corresponding ClubMember::chairedCommittee property has to be updated as well;

  3. whenever a committee object is destroyed, the corresponding ClubMember::chairedCommittee property has to be unassigned.

In the case of a derived inverse reference property that is multi-valued while its inverse base property is single-valued (like Publisher::publishedBooks in Figure 15.4 below being derived from Book::publisher), the life cycle dependencies imply that

  1. whenever a new 'base object' (such as a book) is created, the corresponding inverse property has to be updated by adding a reference to the new base object to its value set (like adding a reference to the new book object to Publisher::publishedBooks );

  2. whenever the base property is updated (e.g., a new publisher is assigned to a book), the corresponding inverse property (in our example, Publisher::publishedBooks) has to be updated as well by removing the old object reference from its value set and adding the new one;

  3. whenever a base object (such as a book) is destroyed, the corresponding inverse property has to be updated by removing the reference to the base object from its value set (like removing a reference to the book object to be destroyed from Publisher::publishedBooks ).

We use the slash-prefix (/) notation in the example above for indicating that the property ClubMember::chairedCommittee is derived on update from the corresponding committee object.