OpenSubsystems

Business Components for Java Applications

Open Core

Tutorial

Documentation
Last modified

$Author: bastafidli $
$Date: 2007/03/11 06:30:45 $
$Revision: 1.25 $
$RCSfile: tutorial_datamodel.html,v $

Designing the data model

One of the first steps when developing new subsystems or application should be to decide what the data model looks like. A good understanding of data that the application works with is critical prerequisite for sucessful implementation. Open Core provides several interfaces and classes that speed up implementation of application data model.

Our tasks

We will decide what data entities our data model consists of. We will decide what are the default behavioral characteristics of our data entities, such as:

  • if the data can be only created and never modified
  • if the data can be changed after creation
  • if an option to create a new version instead of changing the original data should be available

For each proposed data entity we will choose base class provided by Open Core that fits its characteristics the best. Thanks to the inheritance, the data entities will instantly get many attributes and operations we would have to otherwise design and code manually. The only thing left is to add to the proposed data entities any attributes specific to our application.

DataObject - base class for all data entities regardless of how they are persisted and what other behavioral characteristics they may have. Data object implements Transfer Object pattern. The main goal is to encapsulate set of related attributes as one object so that they can be easily passed between different modules and tiers of the application. It supports an architectural assumption that each data object has a single unique identifier called id. It also provides cousin of standard equals() method called isSame(), which compares two data objects based only on their domain attributes. Two data objects are considered the same if they differ only in id and other similar environment dependent attributes such as creation or modification timestamps.

BasicDataObject - base class for data objects that are never modified. It is derived from DataObject and actually implements the id attribute and creation timestamp. It also implements architectural assumption that each data object belongs to some partition called domain.

ModifiableDataObject - base class for data objects that can be modified. It is derived from BasicDataObject and adds modification timestamps used for optimistic locking.

GenericData - interface used to expose information about a data object that can be easily used to identify the data object in the user interface.

OpenChronicle data model

The data model of OpenChronicle is quite simple. It consists of only two entities. Blog View source represents the chronicle and Entry View source represents the chronicle entry. There can be zero or more blogs in the system. Blog consists of zero or more entries.

We want to allow user to modify either one of these entities and therefore both classes will be derived from ModifiableDataObject. This way they will also inherit attributes provided by Open Core that we would have to otherwise implement manually:

  • id - numeric id uniquely identifying the data object (either chronicle or entry)
  • domain id - numeric id identifying partition where the chronicles and entries will be created. It allows us to have multiple partitions separating for example chronicles and entries for various users.
  • creation timestamp - date and time when the data object was created
  • modification timestamp - date and time when the data object was last modified

Blog View source has three additional attributes:

  • folder - allows to organize chronicles within folders so that the entries can be accessed using intuitive url http://..../folder/index.html
  • caption - descriptive name of the chronicle used by user to easily identify it
  • comments - additional description of the chronicle and its content

Entry View source has four additional attributes:

  • caption - title of the chronicle entry used by user to easily identify it
  • comments - the text of the entry
  • image URL - optional link to the image to display with the entry
  • target URL - optional link, which will be used when user clicks on the image

These attributes are implemented as a ordinary member variables within each class with appropriate getters and setters. Most IDEs (such as Eclipse) can generate the getters and setters for these attributes with just a few mouse clicks. This makes the implementation of the data objects very fast. For example, caption attribute is implemented as follows:

   protected String m_strCaption;
               
   public String getComments(
   )
   {
      return m_strComments;
   }
               

Setting the data limits

Both data objects contain several attributes (for example caption and comments) that can hold variable amount of data. The data objects may have set limits how much data they accept into such fields. But where do the limits come from? Either they are constrained by the business logic or by the limitations set by the persistence store (such as sizes of database columns). Because both of these scenarios are external to the data object, the data object only provides static methods that can be used by the business logic or by the persistence layer to set such limits, for example

   protected static int s_iCommentsMaxLength;
               
   public static void setCommentsMaxLength(
      int iCommentsMaxLength
   )
   {
      s_iCommentsMaxLength = iCommentsMaxLength;
   }
   
   public static int getCaptionMaxLengthStatic(
   )
   {
      return s_iCaptionMaxLength;
   }
               

We will use these methods later to set appropriate limits.

Data object comparison

The last step Open Core requires us to do is to implement method isSame. This method is similar to the standard Java method equals and it is in fact called from the default implementation of equals in the DataObject derived base classes (therefore you do not have to override equals if you provide isSame. The difference is, that the isSame method should compare two data objects only based on the attributes, that are semantically important and do not depend on an environment. This means that the attributes, which are dictated by Open Core and are generated (e.g. id, creation and modification timestamps) do not need to be compared. It allows us to compare an in-memory object and object coming from the database and determine if they contain the same data ignoring the differences caused by the objects persistence. For Blog data object the isSame method needs to compare only folder, caption and comments:

   public boolean isSame(
      Object oObject
   )
   {
      boolean bReturn = false;
      Blog    data;

      if (oObject == this)
      {
         bReturn = true;
      }
      else
      {
         if (oObject != null && oObject instanceof Blog)
         {
            data = (Blog) oObject;
            bReturn = ((data.getFolder() == null && m_strFolder == null)
                           || data.getFolder().equals(m_strFolder))
                     && ((data.getCaption() == null && m_strCaption == null)
                           || data.getCaption().equals(m_strCaption))
                     && ((data.getComments() == null && m_strComments == null)
                           || data.getComments().equals(m_strComments));
         }
      }
      return bReturn;
   }
               

Now, when we have a clear idea how our data model looks like we can start implementation of the persistence layer.

Next: Implementing the persistence layer
Previous: Overview