Building a Domain Object Model

A Domain Object Model (DOM) is a set of classes that models concepts from your problem domain. We really cannot do justice to the concept of a DOM in just a small portion of this chapter, so we recommend that you read Patterns of Enterprise Application Architecture by Martin Fowler (Addison-Wesley, 2002) or Domain Driven Design: Tackling Complexity in the Heart of Software by Eric Evans (Addison-Wesley, 2003) for a more complete description of the DOM pattern. Although we do not go into great detail on this pattern, we do show you why we chose to create a DOM for the SpringBlog application and how we built our DOM.

Spring and the Domain Object Model

Given that this is a book on Spring, you might find it strange that we dedicate considerable page space to a topic that is not directly related to Spring in any way. Of the applications that we have built using Spring, the only objects that are consistently not managed by Spring are Domain Objects. The reason for this is that, really, Spring does not need to be involved with Domain Objects. Generally, you create many instances of your Domain Objects using the new() operator, and although you can have Spring create new instances for you as you need them, it seems like overkill to have to call BeanFactory.getBean() every time you need a new Domain Object instance. This is especially true when you consider that, typically, Domain Objects do not take advantage of Dependency Injection, because they generally have few dependencies outside of the DOM itself, and they don't require much configuration.

You might well be wondering, then, why so much attention to the DOM? The answer is simple. The DOM affects so many other parts of the application, parts that are managed by Spring, that getting it right is very important to getting your whole application right.

DOM != Value Object

The important thing to understand about the DOM pattern is that it is not the same as the Value Object (often called Data Transfer Object) pattern. The Value Object pattern was created to overcome a shortcoming in the original EJB specification that meant that all calls to an EJB were remote. Configuring the state of an EJB typically means many calls, all of which are remote. Using a Value Object, object state is transferred in bulk using a single remote call, thus reducing the performance hit of making many remote calls.

Note 

Officially the Value Object pattern is not the same as the Data Transfer Object pattern. Martin Fowler defines a Value Object as "A small simple object, like money or date range, whose equality isn't based on identity." The confusion arises, however, from the Core J2EE Patterns catalog that uses the term Value Object for many of the examples of the Data Transfer Object pattern. In this section, we use Value Object and Data Transfer Object interchangeably, but we are talking about the Data Transfer Object pattern.

A DOM is an object-based representation of the application problem domain, intended to allow the programmer to code in terms of objects that exist in the problem domain, not objects that exist inside the computer. Where a Value Object purely encompasses state, it is perfectly acceptable for a Domain Object to encompass both state and behavior (although you may choose not to encapsulate behavior inside Domain Objects).

Another key difference between Domain Objects and Value Objects is that a Value Object's structure is driven by the need to transfer data remotely, whereas a Domain Object is modeled to represent a real-world concept and is not driven by some need of the application infrastructure. As we discuss later, we believe there are no hard-and-fast rules for modeling Domain Objects; you have to choose a level of granularity that matches your application and the functions it will perform.

It is possible for an application to have both Domain Objects and Value Objects. In this approach, Value Objects are used by the Business Tier to communicate with the Data Access Tier. These Value Objects are then converted as appropriate into Domain Objects and passed into the Presentation Tier for rendering. In our opinion, this approach is really not worth the hassle. With Spring, the data access framework is so powerful that it is simple to map data directly to Domain Objects. However, this approach is sometimes problematic when you have a DOM that is quite far removed from the model in the underlying data store. This issue is discussed in greater detail in the later section entitled "Modeling Domain Objects."

Why Create a Domain Object Model?

Creating a DOM requires some up-front effort in order to identify Domain Objects and then create an in-code representation of these Objects. However, in all but the most trivial of applications, this up-front effort is far outweighed by the time you will save and the bugs you will avoid when it comes to implementing business logic to do something with your Domain Objects. We find that using a good DOM makes creating the code to solve business problems much easier, since you are able to code in terms of the problem rather than in terms of the machine. A good DOM makes it easier for developers to transform application requirements into application features.

Modeling Domain Objects

There are a great many different methodologies and approaches to Domain Object modeling. Some practices advocate letting the underlying data store drive your object model, whereas some practices say, "Let the business domain drive the object model." In practice, we have found that a happy medium between these two approaches results in a DOM that is both easy to work with and well performing.

For small applications with only five or six database tables, it is often easier just to create one Domain Object that corresponds to each database table. Although these objects are not strictly Domain Objects—in that their creation is not driven by the problem domain, but rather the data structure—they are close enough for the purposes of such a simple application. Indeed, in many small applications, the result of an extensive domain modeling process is an object model that matches the database structure entirely.

For larger applications, a little more thought has to be put into the real-world problem domain and the underlying data store. When we are building a DOM for an application, we usually focus on three main points:

  • How the problem domain is structured

  • How the Domain Objects will be used

  • How the underlying data store is constructed

What we are looking for is a DOM that is as close to the ideal model as possible without affecting the performance of the data store too much and without having too great an impact on code that has to use the Domain Objects.

Typically, a DOM is quite granular, and you might end up with more than one class for a single logical concept. For instance, consider the concept of an order in a purchasing system. Typically an order is modeled as a single Order object with one or more OrderLine objects that represent each line item of the order. Trying to model an order using a single object leads to an object model that is unnecessarily coarse and unwieldy, not to mention difficult to implement. You should always look for opportunities to increase the granularity of your Domain Objects when it makes working with the DOM easier.

You will also find that your DOM contains objects that do not exist in your data store. For instance, a typical purchasing system has some notion of a shopping cart, perhaps represented by Cart and CartItem objects. Unless you are required to persist contents across user sessions, chances are these Domain Objects do not have corresponding tables for data storage. Remember, you are not simply building an object-oriented representation of your database, you are modeling the business domain. This point cannot be stressed enough. We have seen plenty of projects that created a pseudo-DOM derived directly from the data store, and inevitably these projects suffered from the lack of abstraction that can be gained from a well-defined DOM.

We have found that a solid DOM comes from taking the time to look at your problem domain, identifying the objects in the domain, and then looking at how the natural granularity of these objects fits into the requirements of your application. Although we take both the utilization of the Domain Objects and the underlying data store into consideration, we don't like to let these have undue influence on our DOM.

It is important to remember that the goal of building a DOM is to create a set of classes that help you and other developers build the application at a level of abstraction that is closer to the application's problem domain. In general, we consider all other concerns secondary when building a DOM. If you find that performance is suffering due to the design of your DOM, feel free to tweak away, but we don't recommend that you do this on a hunch. Make absolutely sure that your DOM is to blame. You don't want to reduce the benefits of your DOM out of the mistaken belief that it is performing badly.

Database Modeling and Domain Object Modeling

Although database modeling and Domain Object modeling are quite similar, the results you get from each are rarely the same, and indeed, you rarely want them to be. When modeling a database, you are looking for the structure that allows you to store and retrieve data in the most efficient and consistent manner. When you are building a DOM, performance is obviously important, but so is building an API that is easy to work with and makes assembling your business logic simple. In general, we have found that it is best to model the database in the way that is best for the database, and model the DOM, initially at least, in the way that is best for the DOM. You can make any changes later on, if and when you identify performance bottlenecks.

Modeling Domain Object Relationships

The most common mistake we see in a DOM, especially when the DOM is driven by the design of the database, is that Domain Objects are created to represent relationships between other Domain Objects. This comes from the fact that a many-to-many relationship between two tables in a database must have a third table to construct the relationship. Relationships in a DOM should be modeled in a much more OOP-style way, with Domain Objects maintaining references to other Domain Objects or lists of Domain Objects.

A common mistake when populating Domain Object data from a database, such as what would be done in the Data Access Tier of an application, is to assume that all related Domain Objects must be loaded from the database as well—this is not so. See the later section entitled "Domain Object Relationships" for a more detailed discussion of this problem.

To Encapsulate Behavior or Not?

You are not forced to have your Domain Objects encapsulate any behavior at all; indeed, you can choose to have your Domain Objects represent just the state of your problem domain. In most cases, we have found that it is better to factor out much of the business logic into a set of service objects that work with Domain Objects rather than encapsulate this logic inside the Domain Objects. Typically, we place all logic that interacts with components outside of the DOM into the service objects. In this way, we are reducing the coupling between the DOM and components involved in application logic. This allows the DOM to be used in a wider variety of scenarios, and often, you will find that the DOM can be reused in other applications that solve problems in the same domain.

Where we like to encapsulate behavior in the DOM is in situations where the logic is implemented purely in interactions between Domain Objects. The jPetStore sample application included with Spring provides a great example of this that can be mapped to our purchasing system example. In this scenario, a user has a shopping cart, represented by a Cart object, and a list of CartItem objects. When the user is ready to purchase the items in her cart and create an order, the application has to create an Order object along with a list of OrderLine objects that corresponds to the data modeled by the Cart and CartItem objects. This is a perfect example of when behavior should be encapsulated inside the DOM. The conversion from Cart to Order is coded purely in terms of Domain Objects with no dependencies on other components in your application. In jPetStore, the Order class has an initOrder() method that accepts two arguments, Account and Cart. All the logic required to create an Order based on the Cart object for the user represented by the Account object is represented in this method.

As with most things related to modeling, there are no hard-and-fast rules about when to put logic inside a Domain Object and when to factor it out into a service object. You should avoid placing logic inside your Domain Objects when it causes your Domain Objects to depend on other application components outside of the DOM. In this way, you are ensuring that your DOM is as reusable as possible. On the flipside of this, logic that involves only Domain Objects is ideally placed in the DOM, which allows it to be used wherever the DOM is used.

The SpringBlog Domain Object Model

Because the SpringBlog application is actually quite simple, the DOM is also quite simple. Figure 11-1 shows the SpringBlog DOM.

image from book
Figure 11-1: The DOM in SpringBlog

Although this is quite a simple DOM, it does highlight some of the points we have been talking about. These are discussed in the next three sections.

Inheritance in the SpringBlog DOM

Central to the SpringBlog application is the concept of a posting. Postings come in two types: entries, which are top-level postings to the blog; and comments, which are comments about a particular blog entry. Although the SpringBlog application contains no security, the intention is that only the blog owner can create entries whereas any anonymous user can create comments. We decided that we would define common postings characteristics in an interface, BlogPosting, shown in Listing 11-4, and have both Entry and Comment implement this interface.

Listing 11-4: The BlogPosting Interface

image from book
package com.apress.prospring.domain;      import java.util.Date; import java.util.List;      public interface BlogPosting {          public List getAttachments();     public void setAttachments(List attachments);          public String getBody();     public void setBody(String body);          public Date getPostDate();     public void setPostDate(Date postDate);          public String getSubject();     public void setSubject(String subject); }
image from book

However, this results in undue code duplication, with both Entry and Comment having their own implementations of BlogPosting. To get around this, we introduce the AbstractBlogPosting class and have Entry and Comment extend this class. AbstractBlogPosting is shown in Listing 11-5.

Listing 11-5: The AbstractBlogPosting Class

image from book
package com.apress.prospring.domain;      import java.util.Date; import java.util.List;      public abstract class AbstractBlogPosting implements BlogPosting {          protected String subject;          protected String body;          protected Date postDate;          protected List attachments;          public String getBody() {         return body;     }          public void setBody(String body) {         this.body = body;     }          public Date getPostDate() {         return postDate;     }          public void setPostDate(Date postDate) {         this.postDate = postDate;     }          public String getSubject() {         return subject;     }          public void setSubject(String subject) {         this.subject = subject;     }          public List getAttachments() {         return attachments;     }          public void setAttachments(List attachments) {         this.attachments = attachments;     }      }
image from book

By extending this base class, we move all the BlogPosting implementation details out of Entry and Comment, reducing code duplication. As an example of this, Listing 11-6 shows the code for the Entry class.

Listing 11-6: The Entry Class

image from book
package com.apress.prospring.domain;      public class Entry extends AbstractBlogPosting {          private static final int MAX_BODY_LENGTH = 80;          private static final String THREE_DOTS = "...";          private int entryId;          public String getShortBody() {         if (body.length() <= MAX_BODY_LENGTH)             return body;         StringBuffer result = new StringBuffer(MAX_BODY_LENGTH + 3);         result.append(body.substring(0, MAX_BODY_LENGTH));         result.append(THREE_DOTS);              return result.toString();     }          public String toString() {         StringBuffer result = new StringBuffer(50);         result.append("Entry { , subject=");         result.append(subject);         result.append(" }");              return result.toString();     }          public int getEntryId() {         return entryId;     }          public void setEntryId(int entryId) {         this.entryId = entryId;     } } 
image from book

This is a pattern that is used extensively in Spring and throughout the SpringBlog application. Common functionality is defined in interfaces rather than abstract classes, but we provide a default implementation of the interface as an abstract class. The reason for this is that, where possible, we can take advantage of the abstract base class as with Entry and Comment, thus removing the need for each class to implement the BlogPosting interface directly. However, should a requirement arise for the Entry class to extend the Foo class, then we can simply mplement the BlogPosting interface directly in Entry. The main point to remember here is that you do not define common functionality in terms of abstract classes because doing so restricts you to a set inheritance hierarchy. Instead, define common functionality in terms of interfaces, along with default implementations of these interfaces as abstract base classes. This way you can take advantage of the inherited implementation wherever possible, but you are not artificially constraining your inheritance hierarchy.

A point of note here is that we did not reflect this inheritance tree in the database. That is to say, we didn't create a BlogPosting table to store the shared data, and then two tables, Entry and Comment, to store the entity-specific data. The main reason for this is that we didn't think that an application of the size of SpringBlog warranted the complexity of that structure, plus this example highlights our point about having a DOM that is different in structure than the database. The main reason for defining this inheritance hierarchy, besides that it is a good design, is to allow the SpringBlog application to work with the common data in the Entry and Comment objects, without having to differentiate between the two. A good example of this is the obscenity filter that we covered in Chapter 7.

Domain Behavior in SpringBlog

Although the SpringBlog domain model is simplistic, we still need to encapsulate some logic in the domain model. Because the body of a blog posting could potentially be very long, we wanted a mechanism to get a snippet of the body to use when it displays a list of blog postings. For this reason, we create the Entry.getShortBody() method shown in Listing 11-7.

Listing 11-7: Behavior in the Entry Class

image from book
package com.apress.prospring.domain;      public class Entry extends AbstractBlogPosting {          private static final int MAX_BODY_LENGTH = 80;          private static final String THREE_DOTS = "...";          public String getShortBody() {         if (body.length() <= MAX_BODY_LENGTH)             return body;         StringBuffer result = new StringBuffer(MAX_BODY_LENGTH + 3);         result.append(body.substring(0, MAX_BODY_LENGTH));         result.append(THREE_DOTS);              return result.toString();     }     /* omitted for clarity */ }
image from book

Here you can see that to build the short body, we take the first 80 characters of the body and simply append three dots to the end. This is a simplistic implementation, but it does highlight a typical scenario for encapsulating logic in the DOM.

Domain Object Relationships

In the DOM in Figure 11-1, notice that we defined an association between Entry and Attachment, and Comment and Attachment. As part of the SpringBlog requirements, we want to be able to upload and store files with both types of posting. In the database, we have a table to store the attachments called, strangely enough, attachments. Then to associate attachments with an entry or a comment, we have two other tables: entryattachments and commentattachments. A common mistake we see is that people create Domain Objects to model these relationships, rather than using standard Java features to relate the objects together. When you have a oneto-one relationship in your database, you can model this in the DOM by having one object maintain a reference to the other. For one-to-many or many-to-many relationships, using Java Collections makes it simple to represent these complex relationships in a familiar manner that is simple to work with in code. Listing 11-8, a snippet from the AbstractBlogPosting class, shows how we use a List to store the Attachment objects for each posting.

Listing 11-8: Using List for Domain Object Relationships

image from book
package com.apress.prospring.domain;      import java.util.List;      public abstract class AbstractBlogPosting implements BlogPosting {          protected List attachments;          public List getAttachments() {         return attachments;     }          public void setAttachments(List attachments) {         this.attachments = attachments;     } }
image from book

Rather than using additional objects to model relationships, we use a simple List to model the one-to-many relationship. Aside from reducing the amount of code we need to type, this method prevents the DOM from becoming polluted with needless classes, and allows familiar Java concepts such as Iterators to be used when navigating relationships.

Canonicalization and Memory Considerations for a DOM

A consideration when modeling objects in your domain is the amount of memory taken up by these objects. Typically you have many instances of the same class in your application at the same time. Often you have the same logical entity represented by many different instances of a Domain Object at the same time. In many cases, you can't avoid this, but for some scenarios, you can avoid this by preventing multiple instances of a Domain Object from being created to represent the same logical entity—this technique is called canonicalization.

Note 

This canonicalization pattern is often referred to as the Typesafe Enum pattern.

Before we look at this technique, we should first discuss scenarios where applying it is valid. Consider again the purchasing system. One of the Domain Objects for the purchasing system is Product. Now it is possible for more than one user to be looking at the same product at the same time. Typically, this results in multiple instances of Product being created to represent the same physical product. Our fictional purchasing system sells over 10,000 different product lines, and it is this number that makes canonicalization impractical, as you will see.

Another Domain Object in the purchasing system is ShippingCompany, which represents one of the companies that ships orders to the user. Our system only offers three choices of shipping company, yet there may be many more instances of shipping companies around in the JVM at any one time. This low number of fixed data sets makes the ShippingCompany ideal for canonicalization. Basic canonicalization works by making the constructor of the class private and then defining all possible instances of the class as public static final members. Listing 11-9 shows an example of this for the ShippingCompany class.

Listing 11-9: Canonicalization for Domain Objects

image from book
package com.apress.prospring.ch11.domain;      public class ShippingCompany {          public static final ShippingCompany UPS = new ShippingCompany(1, "UPS");          public static final ShippingCompany DHL = new ShippingCompany(2, "DHL");          public static final ShippingCompany FEDEX = new ShippingCompany(3, "FEDEX");          private final int id;          private final String name;          private ShippingCompany(int id, String name) {         this.id = id;         this.name = name;     }          public int getId() {         return this.id;     }          public String getName() {         return this.name;     }          public static ShippingCompany fromInt(int id) {         if (id == UPS.id) {             return UPS;         } else if (id == DHL.id) {             return DHL;         } else if (id == FEDEX.id) {             return FEDEX;         } else {             return null;         }     } }
image from book

Here you can see that the three instances of the ShippingCompany class are created as public static final members. It is not possible for external classes to create more instances of ShippingCompany because the constructor is declared private. The fromInt() method isn't necessary and is in fact something that we inherited from Hibernate. The fromInt() method is useful when loading canonicalized objects from the data store. We discuss canonicalization from a data access point of view later in the chapter.

When you have a large number of objects to canonicalize—say you have an application that performs lots of processing on Country objects—you may find that caching is a better solution than canonicalization. We are not going to discuss caching in this chapter. For a more detailed discussion, read Expert One-on-One J2EE Development without EJB by Rod Johnson and Juergen Hoeller (Wrox, 2004).

Domain Object Model Summary

In this section, we looked at the DOM for the SpringBlog application and we spent some time discussing the basics of Domain Object modeling and implementation. There is no doubt that this topic is much greater than what we have covered here. Indeed, a whole range of books is available that discusses the topic in detail. We only scratched the surface here, and we focused on why you want to build a DOM, what the focus is when building one, and some general topics related to the SpringBlog application.

Although it is certainly possible to build applications without defining and building a DOM, it is our experience that taking the time to do so pays off in reduced complexity, lower maintenance costs, and fewer bugs.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net