The CaveatEmptor example is an online auction application that demonstrates ORM techniques and Hibernate functionality. You can download the source code for the application from www.jpwh.org. We won’t pay much attention to the user interface in this book (it could be web based or a rich client); we’ll concentrate instead on the data access code. When a design decision about data access code that has consequences for the user interface has to be made, we’ll naturally consider both.
In order to understand the design issues involved in ORM, let’s pretend the CaveatEmptor application doesn’t yet exist and that you’re building it from scratch. Let’s start by looking at the architecture.
1 - A layered architecture
With any nontrivial application, it usually makes sense to organize classes by concern. Persistence is one concern; others include presentation, workflow, and business logic. A typical object-oriented architecture includes layers of code that represent the concerns.
Cross-cutting concerns
There are also so-called cross-cutting concerns, which may be implemented generi-cally—by framework code, for example. Typical cross-cutting concerns include logging, authorization, and transaction demarcation.
A layered architecture defines interfaces between code that implements the various concerns, allowing changes to be made to the way one concern is implemented without significant disruption to code in the other layers. Layering determines the kinds of inter-layer dependencies that occur. The rules are as follows:
■ Layers communicate from top to bottom. A layer is dependent only on the interface of the layer directly below it.
■ Each layer is unaware of any other layers except for the layer just below it.
Different systems group concerns differently, so they define different layers. The typical, proven, high-level application architecture uses three layers: one each for presentation, business logic, and persistence, as shown in figure 3.1.
■ Presentation layer—The user interface logic is topmost. Code responsible for the presentation and control of page and screen navigation is in the presentation layer. The user interface code may directly access business entities of the shared domain model and render them on the screen, along with controls to execute actions. In some architectures, business entity instances might not be directly accessible by user interface code: for example, if the presentation layer isn’t running on the same machine as the rest of the system. In such cases, the presentation layer may require its own special data-transfer model, representing only a transmittable subset of the domain model.
■ Business layer—The exact form of the next layer varies widely between applications. It’s generally agreed that the business layer is responsible for implementing any business rules or system requirements that would be understood by users as part of the problem domain. This layer usually includes some kind of controlling component—code that knows when to invoke which business rule. In some systems, this layer has its own internal representation of the business domain entities. Alternatively, it relies on a domain model implementation, shared with the other layers of the application.
■ Persistence layer—The persistence layer is a group of classes and components responsible for storing data to, and retrieving it from, one or more data stores. This layer needs a model of the business domain entities for which you’d like to keep persistent state. The persistence layer is where the bulk ofJPA and Hibernate use takes place.
■ Database—The database is usually external, shared by many applications. It’s the actual, persistent representation of the system state. If an SQL database is used, the database includes a schema and possibly stored procedures for execution of business logic close to the data.
■ Helper and utility classes—Every application has a set of infrastructural helper or utility classes that are used in every layer of the application (such as Exception classes for error handling). These shared infrastructural elements don’t form a layer because they don’t obey the rules for inter-layer dependency in a layered architecture.
Now that you have a high-level architecture, you can focus on the business problem.
2 - Analyzing the business domain
At this stage, you, with the help of domain experts, analyze the business problems your software system needs to solve, identifying the relevant main entities and their interactions. The motivating goal behind the analysis and design of a domain model is to capture the essence of the business information for the application’s purpose.
Entities are usually notions understood by users of the system: payment, customer, order, item, bid, and so forth. Some entities may be abstractions of less concrete things the user thinks about, such as a pricing algorithm, but even these are usually understandable to the user. You can find all these entities in the conceptual view of the business, sometimes called a business model.
From this business model, engineers and architects of object-oriented software create an object-oriented model, still at the conceptual level (no Java code). This model may be as simple as a mental image existing only in the mind of the developer, or it may be as elaborate as a UML class diagram. Figure 3.2 shows a simple model expressed in UML.
This model contains entities that you’re bound to find in any typical e-commerce system: category, item, and user. This model of the problem domain represents all the entities and their relationships (and perhaps their attributes). We call this kind of object-oriented model of entities from the problem domain, encompassing only
Figure 3.2 A class diagram of a typical online auction model
those entities that are of interest to the user, a domain model. It’s an abstract view of the real world.
Instead of an object-oriented model, engineers and architects may start the application design with a data model (possibly expressed with an entity-relationship diagram). We usually say that, with regard to persistence, there is little difference between the two; they’re merely different starting points. In the end, what modeling language you use is secondary; we’re most interested in the structure and relationships of the business entities. We care about the rules that have to be applied to guarantee the integrity of data (for example, the multiplicity of relationships) and the code procedures used to manipulate the data.
In the next section, we complete our analysis of the CaveatEmptor problem domain. The resulting domain model will be the central theme of this book.
3 - The CaveatEmptor domain model
The CaveatEmptor site auctions many different kinds of items, from electronic equipment to airline tickets. Auctions proceed according to the English auction strategy: users continue to place bids on an item until the bid period for that item expires, and the highest bidder wins.
In any store, goods are categorized by type and grouped with similar goods into sections and onto shelves. The auction catalog requires some kind of hierarchy of item categories so that a buyer can browse these categories or arbitrarily search by category and item attributes. Lists of items appear in the category browser and search result screens. Selecting an item from a list takes the buyer to an item-detail view where an item may have images attached to it.
An auction consists of a sequence of bids, and one is the winning bid. User details include name, address, and billing information.
The result of this analysis, the high-level overview of the domain model, is shown in figure 3.3. Let’s briefly discuss some interesting features of this model.
Each item can be auctioned only once, so you don’t need to make Item distinct from any auction entities. Instead, you have a single auction item entity named Item. Thus, Bid is associated directly with Item. You model the Address information of a User as a separate class, a User may have three addresses, for home, billing, and shipping. You do allow the user to have many BillingDetails. Subclasses of an abstract class represent the various billing strategies (allowing future extension).
The application may nest a Category inside another Category, and so on. A recursive association, from the Category entity to itself, expresses this relationship. Note that a single Category may have multiple child categories but at most one parent. Each Item belongs to at least one Category.
This representation isn’t the complete domain model but only classes for which you need persistence capabilities. You’d like to store and load instances of Category, Item, User, and so on. We have simplified this high-level overview a little; we may introduce additional classes later or make minor modifications to them when needed for more complex examples.
|
Figure 3.3 Persistent classes of the CaveatEmptor domain model and their relationships |
Certainly, the entities in a domain model should encapsulate state and behavior. For example, the User entity should define the name and address of a customer and the logic required to calculate the shipping costs for items (to this particular customer).
There might be other classes in the domain model that have only transient runtime instances. Consider a WinningBidStrategy class encapsulating the fact that the highest bidder wins an auction. This might be called by the business layer (controller) code when checking the state of an auction. At some point, you might have to figure out how tax for sold items is calculated or how the system may approve a new user account. We don’t consider such business rules or domain model behavior to be unimportant; rather, this concern is mostly orthogonal to the problem of persistence.
Now that you have a (rudimentary) application design with a domain model, the next step is to implement it in Java.
Object persistence with full ORM is most suitable for applications based on a rich domain model. If your application doesn't implement complex business rules or complex interactions between entities (or if you have few entities), you may not need a domain model. Many simple and some not-so-simple problems are perfectly suited to table-oriented solutions, where the application is designed around the database data model instead of around an object-oriented domain model, often with logic executed in the database (stored procedures). Another aspect to consider is the learning curve: once you're proficient with Hibernate, you'll use it for all applications, even as a simple SQL query generator and result mapper. If you're just learning ORM, a trivial use case may not justify your invested time and overhead.
Comments
Post a Comment