Chapter 5 Domain Model

Abstract

This chapter introduces the concept of the Domain model and describes the various types a domain model is composed of.

"To create software that is valuably involved in users' activities, a development team must bring to bear a body of knowledge related to those activities. The breadth of knowledge required can be daunting. The volume and complexity of information can be overwhelming. Models are tools for grappling with this overload. A model is a selectively simplified and consciously structured form of knowledge. An appropriate model makes sense of information and focuses it on a problem." [2] [DDD2003]

MDD

Figure 5.1. MDD


In the following sections we describe each of the supported domain model elements and explain how they map to the previous figure.

"Some objects are not defined primarily by their attributes. They represent a thread of identity that runs through time and often across distinct representations. Sometimes such an object must be matched with another object even though attributes differ. An object must be distinguished from other objects even though they might have the same attributes. Mistaken identity can lead to data corruption. An object defined primarily by its identity is called an ENTITY."

Entities map to the same concept in ⇒MDD and represents an abstraction of the data and behaviour of a real-world concept togehter with an unique identity. An entity is composed of attributes, references, sortorders, finders and one optional key definition as outlined in the figure below. Entities are identified by name and can have a number of attributes and references. To support inheritance, an entity can also refer to another entity as its supertype.


Besides the id attribute there is another mandatory technical attribute required to support the data integrity of entities and to prevent lost updates. The pattern of the underlying concept is known as optimistick locking and was described in Patterns of Enterprise Architecture. [3][PEAA2003]

The supported datatypes for version attributes are:

  • Long

  • Integer

  • Timestamp

  • Date

Note:Timestamp and Date version attributes are treated in the same way internally and thus expected to exhibit a milliseconds fraction.

An attribute defines a certain feature of the enclosing entity and consists of a type definition, a name and optional constraints and properties as shown in the next example.

entity Customer extends BaseEntity {
    String(25) firstName
      required = true
      readonly = true
      available = true
      derived = true
      transient = true
      constraints = StringValidator[firstName<=30]
      format = StringValidator
      title= "First name"
      description = "First name"
      hstore = "hstore column name"
      unit = "some unit value"
}

Besides the declaration of static boolean literal values as shown above, right-hand-side values of properties also accept expression language terms to define certain property conditions which are evaluated at runtime. This term must either be expressed inline together with the property declaration or 'externally' a as named condition variable inside a context definition block. The following listing shows an example for named condiditions because this is the suggested way as it allows to share conditions declarations amongst several properties. As always it's possible to extract the condition variables into an separate model file which can be later imported with the classpath URI mechanism described elsewhere.

entity Customer  {
 String(25) firstName
  required = ovA
}

context AppContext {
 property String operationVariant
 // define named conditions
 conditions { 
  ovA= operationVariant == "A", doc:"Operation variant A is active"
  ovB= operationVariant == "B", doc:"Operation variant B is active"
 }
}

References are used in modeling one end of an association between two involved entities. The only thing which differentiates an attribute from a reference declaration is the kind of type used. Since references are used to indicate relations between two involved entities, references have to start with an entity type followed by the name of the reference (i.e. role name in UML). If the association should be navigable in both directions, there has to be an additional reference to represent this bi-directionality indicated with the oppositeof keyword followed by the name of the opposite reference. The specification of an opposite reference triggers the creation of additional operations and logic which enforces the correct 'linking' between the two involved entities. This is especially important in combination with the default hibernate based repository (i.e. Dao) implementation because hibernate requires the setting on both reference sides to correctly handle the persistence aspect (insert/update) of those references.

In general we distinguish between two different types of referencens which differ in the level of the relatedness between the two particapting entities.

The first one are a simple matter of defining a bi- or uni-directional relationship between two particpating entities where each of the two entities has its own independent lifecycle. This kind of reference is comparable to the standard uml association semantics. The following snippet shows one example for a uni-directional reference between Customer and Address together with a many-valued bi-directional association between Product and Category.

entity Customer extends BaseEntity {
    String firstName
    String lastName
    Address invoiceAddress
       required=true
}

entity Address extends BaseEntity {
    String streetName
    String streetNumber
    String zip
    String city
}

entity Category extends BaseEntity {
    String name
    String displayName
    Product[] products oppositeof category
}

entity Product extends BaseEntity {
    String  name
    Integer unitPrice
    Integer unitOnStock
    Integer unitOnOrder
    Category category
}

Currently we only support java.util.Set as the implementation type for many-valued (or collection) references. If necessary we will also support the other container types like List or Map.

Bi-directionallity is declared through the declaration of the oppositeof keyword togehter with a reference to the opposite reference. Generated mutator/accessor methods, of references with a declared opposite reference, contain suplementatry code to set and reset the opposite reference like shown in the next codesnippet.

public void addAddress(Address address) {
  if (address == null) {
   throw new IllegalArgumentException("parameter 'address' must not be null");
  }
  if (!getAddress().contains(address)) {
   this.address.add(address);
   address.setCustomer(this);
  }
}

public void removeAddress(Address address) {
  if (address == null) {
   throw new IllegalArgumentException("parameter 'address' must not be null");
  }
  if (getAddress().contains(address)) {
            this.address.remove(address);
   address.setCustomer(null);
  }
}

As shown in the previous example it's also feasible to define the persistent state of one-ended references as mandatory or not-null with the declaration of the required keyword.

This sections cover examples showing the currently supported reference types and how they map to hibernate concepts.

Note: Currently all references are based upon simple foreign-key relations. Besides bi-directional Many-to-many associations we dont have any support for join tables at the moment.

The following customer entity has a collection of orders which corresponds to a uni-directional One-to-many association in hibernate.

entity Customer extends BaseEntity {
    Order[] orders
}

entity Order extends BaseEntity {
    
}

To make the previous example bi-directional two additional specifications are needed. First the order entity has to declare a reference to customer and the customer entity must indicate the orders collections as the bi-directional opposite end of the order reference.

Note: the oppositeof keyword marks the two involved reference as bi-directional and is only used on the side which doesnt contain the corresponding foreign key column. (e.g. on many-valued reference sides) The following examples maps to a bi-directional One-to-many - Many-to-one relation in hibernate.

entity Customer extends BaseEntity {
    Order[] orders oppositeof customer
}

entity Order extends BaseEntity {
    Customer customer   
}

If we set the previous customer reference to required (i.e. not null foreign key) we should also indicate this fact on the orders reference and mark it as composition since the order entity cannot exists without a valid customer reference (foreign key).

entity Customer extends BaseEntity {
    composition Order[] orders oppositeof customer
}

entity Order extends BaseEntity {
    Customer customer
        required = true
}

The following Product entity has a required (e.g. not-null) uni-directional supplier reference which maps to a (not-null) Many-to-one relation in hibernate.

entity Product {
    Supplier supplier
        required = true
}

entity Supplier {
}

The following example show a bi-directional association on a foreign key.

Note: Again you have to indicate the foreign-key side with the declaration of the oppositeof keyword. In this example the foreign key is on the customer side since we have marked the customer reference on address as opposite side to the address reference.

entity Customer extends BaseEntity {
    Address address
}

entity Address extends BaseEntity {
    Customer customer oppositeof address
}

Bi-directional Many-to-many references are also supported but must be augmented with additional informations in the repository to specify the name of the used join table. The following example shows a bi-directional reference between product and category.

entity Category extends BaseEntity{
    Product[] products oppositeof categories
}

entity Product extends BaseEntity {
    Category[] categories 
}


repository CategoryDao for Category {
    many-to-many products <-> "T_PRODUCT_CATEGORY"
}

repository ProductDao for Product {
    many-to-many categories <-> "T_PRODUCT_CATEGORY"
}

Note: With the currently set of supported hibernate reference mapping types we follow the recommendations from the hibernate best practice chapter explained below:

  • Do not use exotic association mappings

    Practical test cases for real many-to-many associations are rare. Most of the time you need additional information stored in the "link table". In this case, it is much better to use two one-to-many associations to an intermediate link class. In fact, most associations are one-to-many and many-to-one. For this reason, you should proceed cautiously when using any other association style. However, if you use many-to-many association, use master-slave pattern: Mmark only one side (the slave side) with "oppositeof". The other side (the master side) then is responsible for managing database entries.

  • Prefer bidirectional associations:

    Unidirectional associations are more difficult to query. In a large application, almost all associations must be navigable in both directions in queries.



[2] Evans, 2003, S. 3.

[3] Fowler, 2003, S. 416.