After a looong time, I'm blogging XPages again :)

Last time, I was blogging about mini patterns (this and that). These are partly reusable design patterns for common cases.

Today I'd like to introduce a simple pattern which I have been using in a couple of applications. This is also an example how you get benefits from Managed Beans for specific purposes. Let's start.

Data Model for Parent-Child Documents


This is a very common case for Domino applications. There is a main document and its child documents having a pointer (mostly the universal id) back to the main document. Such a model is actually a reflection of the 1-N relation in the RDBMS paradigm. In my little sample, I have chosen a quite simple example of Event-Participants.

Image:Mini Patterns for XPages: Using Beans for Parent - Child Documents

The pointer, or the common key element is generally selected as the universal ID of the main document. I personally try to create my own unique key all the time to have some flexibility to copy/paste documents between the test and production environments. Inheriting values between parent and child records is another common issue. In the RDBMS paradigm, this is strictly forbidden because of the normalization issue. However in Domino applications, we have been using the 'wrong' design, because Domino views have no capability to join different records from different data sets. On the other hand, since XPages provides more flexible and capable data model, so with a little performance cost, we don't need to duplicate data anymore.

UI Model for Parent-Child Documents


Usually, we got used to display parent-child data with an embedded view in classical Domino applications. Because we didn't have any other alternatives. However, in XPages, we have tons of different layout options to present the child data.

Image:Mini Patterns for XPages: Using Beans for Parent - Child Documents

No need to say, choosing a right UI design depends on your application. Sometimes a large table would be all you need, or you might need a card display pattern, especially if users are mobile... You would also mix two approaches (List on the left, Details on the right or expandible details within the row, etc.)

Since I'm not a UI expert, I won't get into details of different design patterns here. The crucial part is that UI model should be assisted with an appropriate back-end controller model. Let's summarize a short list of common limitations in the former paradigm of Domino applications:

  1. Child documents should be saved to be listed on the screen (Views cannot display in-memory documents).

  2. You shouldn't create a child document without saving the parent first. Because if the user create and bind child documents and skip saving the parent, there will be orphan child documents.

  3. Embedded Views are only options to display child documents. If you need sorting/filtering, additional views will be needed.

  4. You can't define a specific action on a specific child (e.g. the best effort would be allowing user to select some child documents and apply a specific action).

  5. Actions on child documents runs within NotesUIView context, therefore interaction with the parent document is limited (e.g. you need to do some hacks through notes.ini parameters).

  6. Lots of performance issues in views. Although views are cached, large datasets with reader fields will be slower. Users will also experience additional network usage on Notes clients.

  7. Many more...


Example for Parent-Child Document Controller


Our example database is on github...

It's a minimally-designed, quick and dirty database, just to illustrate a mini pattern here. I have imported some contact data from David Leedy's Fake Names Demo.

The scenerio is basic. We create an event, add some participants from the fake name pool. Some visual markings represent several states for participants:

Image:Mini Patterns for XPages: Using Beans for Parent - Child Documents

Under the cover, application is using a Managed Bean to handle the list of participants. ParticipantList stores in-memory list for Participant objects. It performs all data operations (e.g. load the participant list for an event document, save participants, etc.) and list operations (e.g. add/remove/sort/etc.). Participant objects are self-contained. They keep track of selection/deletion status and other data about themselves.

When the participant list called for the first time (via plist.list data binding), it prepares the _list object within the managed bean.
// From ParticipantList class... 
public List<Participant> getList() {
     if(_list==null) {
             _list=new ArrayList<Participant>();

             DominoDocument dominoDoc=getDominoDoc();

             try {
                     if(dominoDoc.isNewNote()) {
                             // It will be empty on a new document
                     } else {
                             loadList(getEventCode());
                     }
             } catch (NotesException e) {
                     // Use OpenNTF Domino API to get rid of this.
                     e.printStackTrace();
             }
     }

     return _list;
}

private void loadList(String eventCode) {
     Database db=ExtLibUtil.getCurrentDatabase();

     View view=null;
     ViewEntryCollection entries=null;

     try {
             view=db.getView("ParticipantsByEventCode");
             entries=view.getAllEntriesByKey(eventCode, true);

             ViewEntry ve=entries.getFirstEntry();

             while(ve!=null) {

                     Document pDoc=null;
                     try {
                             pDoc=ve.getDocument();

                             _list.add(new Participant(pDoc));
                     } finally {
                             Utils.recycleObject(pDoc);
                     }

                     ViewEntry tmpEntry=ve;
                     ve=entries.getNextEntry(tmpEntry);
                     Utils.recycleObject(tmpEntry);
             }

     } catch (NotesException e) {
             // Use OpenNTF Domino API to get rid of this.
             e.printStackTrace();
     } finally {
             Utils.recycleObjects(view, entries);
     }
}


The saveParticipants() method into the postSaveDocument event of the parent document source.
// From ParticipantList class... 
public void saveParticipants() {
     try {
             Iterator<Participant> iterator=_list.iterator();
             
             while(iterator.hasNext()) {
                     Participant p=iterator.next();
                     
                     p.saveToDb(ExtLibUtil.getCurrentDatabase());
                     
                     if(p.isDeleted()) {
                             iterator.remove();
                     }
             }

     } catch (NotesException e) {
             // Use OpenNTF Domino API to get rid of this.
             e.printStackTrace();
     }

}

Here, the data layer is also embedded within the Participant class. it loads and saves itself from/to a participant document. The class design decides what to do on I/O operations.

Once you build such a basic structure, it gets really easy to add more functionality. For instance in the example, I have not implemented a duplicate check. It's just a line of code thanks to Java capabilities. Also sorting is not being saved which can be handled in the load/save life cycle of the child document. List container would track the changes in child documents so it can skip saving of unchanged documents, etc.

One more opportunity is adding additional in-place actions that make changes in the child data. I have added LCV status as an example. You may select some participants and toggle their LCV status.

// From ParticipantList class... 
public void toggleLcv() {
   for(Participant p:_list) {
           if(p.isSelected()) {
                   p.toggleLcv();
                   p.setSelected(false);
           }
   }
}

Since the child object is a seperate bean, all business logic related to the child data can be placed inside the bean. The backing-bean unites all data you have into a single stateful entity. If your document is not saved yet, your child documents are unsaved as well. Just like a multivalue field, you have the total control of everything in a single design.

This can be implemented in a business card format with a profile picture and additional details can be included. One of applications I have designed is a Training application, where attendees are imported from HR database. I have used additional visual indicator shown inside the red rectangle. This person has no e-mail address, so another person should be selected to make contact to him/her. Otherwise, user cannot proceed with saving.

Image:Mini Patterns for XPages: Using Beans for Parent - Child Documents

To sum up, this model provides several opportunities


  1. One bean rules all of your child-data. You don't need to worry about the life cycle as long as you bind correct methods to right points.

  2. All child data is contained within a bean (Participant objects in this example). They can handle their own states and business logic.

  3. Thanks to Java Collection Framework, you can perform all kinds of collection operations in the speed of light. You might sort or filter records without additional memory consumption.

  4. List container can handle all controller functionality, such as validation, import/export records etc.

  5. You have full control over I/O operations. A new document is a new document with an empty list of child records. An existing parent document loads its own list of child documents and save modified state of the list when saved.

  6. In memory documents can hold joined data. As an example, if you show a customer and their orders, you shouldn't store product information. Only product id will be enough. During the list aggregation, you might import additional data from other datasets just to include into the presentation.


To create a more reusable codebase is possible. For instance, if you define a child document interface and create an abstract Java class from the interface, you might reuse this child object structure for many different types of parent-child objects.

I'm looking forward to your feedbacks. Have fun :)
Serdar Basegmez   |   September 1 2014 03:15:00 AM   |    Development  XPages    |  
  |   Next   |   Previous

Comments (6)

Gravatar Image
Hunter Medney       09/13/2014 11:52:09 PM

Hi @Serdar, just something that came to mind while reading through your pattern. I recently worked with a parent/child relationship (forum topic and replies) and implemented a compromise solution to rebuild a client-side model (loosely analogous to the child managed bean you discuss) from the backend if the current user made a change to the children. The net result was if the user added, removed or modified a child, they would then see any updates made by other users.

Gravatar Image
Serdar Basegmez    http://lotusnotus.com/en    09/03/2014 10:01:55 AM

@Hunter, a complicated and interesting scenario on multi-user access. It can be done, of course but the demo structure is a viewscope and it does not contain a multiple access check. It's not big deal though. It can be moved to another scope and easily check itself against simultaneous state changes. Do you have a specific case in your mind?

The second question, In the original form (as Training app I have mentioned above), it was using exactly the format you suggested. Then I have transformed it into child document format because of some reporting needs. Also, I'm using a very similar pattern in the 'history' module where I'm displaying user comments and document events on the same discussion-like structure and data is stored in a MIME Bean.

It's all about two methods on child classes. How to load? How to save?

To adapt it myself, I have also created interface and abstract classes to use it in several different forms. But I have skipped that part not to confuse readers.

Gravatar Image
Hunter Medney       09/03/2014 7:47:02 AM

@stephan, @serdar what do you think about storing child entities in JSON in a rich text item on the parent?

Gravatar Image
Hunter Medney       09/03/2014 7:40:29 AM

Thank you for sharing this great pattern and exposing ways to do things beyond traditional Domino development. One suggestion is discussion on incorporating changes to the child documents made by other users while the bean is in scope.

Gravatar Image
Serdar Basegmez    http://lotusnotus.com/en    09/01/2014 2:03:35 PM

Hehe... Indeed :)

I haven't included that scenario to keep it simple for old-school developers. Once you have the controller structure, it's up to the developer how s/he stores the data. It might be a MIMEBean, persistent XML or even a RDBMS data related with the entity you are working on.

An extreme scenario where you are working with some data from your NSF and related entries from your ERP/CRM/SCM or other 3-letter-expensive-software...

Gravatar Image
Stephan H. Wissel    http://www.wissel.net/blog/d6plinks/SHWL-9NJLY6    09/01/2014 1:53:12 PM

Now sprinkle in persistence in XML into a single document... ;-)