Since its inception, J2EE (Java 2 Platform, Enterprise Edition) has simplified enterprise application construction in Java. As J2EE becomes more broadly adopted, however, developers are realizing the need for defined approaches that both simplify and standardize application building. You can begin achieving that goal by standardizing your application's architectural layer.
The architectural layer generally encapsulates an application's technical complexities independent of the business logic, thereby providing a loose coupling between the business functionality and the underlying technical infrastructure. In this article, I explain an emerging method for building the application architecture for J2EE projects—one that employs design patterns to provide the standardization and simplicity that good architecture demands.
Application architecture and J2EE
J2EE is a great infrastructure technology. It provides a uniform standard for the technology stack's lower-level tasks, such as database communication or application distribution. J2EE does not, however, lead developers to build successful applications. J2EE's creators, looking down into the technology stack, wondered: "How can we standardize these APIs?" They should have looked up at the application developers and asked: "How can I give developers the building blocks they need to focus on their business application?"
When beginning a new J2EE project, some team members often ask: "If J2EE is itself an architecture, why do we need more?" Many developers held that misconception in J2EE's early days, but seasoned J2EE developers understand that J2EE fails to provide the application architecture necessary to consistently deliver high-quality applications. These developers often use design patterns to fill that gap.
In programming, design patterns let you leverage the developer community's collective experience by sharing problems and solutions that benefit everyone. A design pattern must capture a problem's definition and context, a possible solution, and the solution's consequences.
For the purposes of J2EE application architecture, design patterns fall into two categories: general software development patterns and those patterns that identify specific J2EE challenges. J2EE-specific design patterns identify the minimal set of known problems that a solid application architecture should solve. The former group, that of software development patterns not specific to J2EE, proves equally powerful—not for identifying problems, but for guiding architecture construction.
Let's examine each area in more detail.
J2EE design patterns
J2EE design patterns have been evolving over the last few years as the Java community has gained J2EE experience. These design patterns identify potential problems encountered when using the various J2EE-specified technologies and help developers construct an application architecture's requirements. The popular Front Controller design pattern, for example, transforms unstructured servlet code into a controller reminiscent of the refined GUI (graphical user interface) development.
J2EE design patterns identify those domain problems most likely to appear in your J2EE projects. Indeed, if the problems were rare, the design patterns would not have evolved to meet them. With that in mind, you'll benefit from addressing each domain problem in your architecture. To solve them all, create a checklist to validate your architecture for completeness. That process contrasts with the process for the software development design patterns I discuss next, as you need apply those patterns only when and if appropriate.
So where do you find J2EE design patterns? Sun Microsystems offers two books that contain many J2EE patterns:
- The J2EE BluePrint Group's Designing Enterprise Applications with the Java 2 Platform (Enterprise Edition), Nicholas Kassem et al. (Addison-Wesley, 2000; ISBN: 0201702770)
- The Sun Professional Services Group's Core J2EE Patterns: Best Practices and Design Strategies, Deepak Alur, John Crupi, and Dan Malks (Prentice Hall, 2001; ISBN: 0130648841)
(See Resources for links to both books.)
Beyond Sun's resources, other publications offer J2EE design pattern information, including various Java industry magazines or Websites (such as JavaWorld), as well as numerous books. (See Resources for links to some of these sites, including JavaWorld's Design Patterns Topical Index page.)
Software development design patterns
Also be aware of the software development design patterns, split into general object-oriented (OO) design patterns and Java-specific design patterns. The Factory pattern, for example, represents a powerful OO design pattern for encapsulating object creation to enable reuse and meet a system's changing requirements. For their part, Java-language design patterns account for Java language specifics. Some are unique to Java and are usually informal (for example, exceptions and primitives), while others are OO patterns refined to apply to Java. The famous Gang of Four book, Design Patterns by Eric Gamma et al., details numerous general software development patterns useful to all programmers.
Don't dismiss these patterns simply because they are not J2EE specific. On the contrary, such patterns can prove just as powerful, if not more so, than J2EE design patterns, because:
- While the J2EE design patterns are new and evolving (because J2EE is new and evolving), the other patterns benefit from age, since the industry has had more time to review and refine them.
- They often serve as the basis from which the J2EE design patterns stem.
- They build the foundation upon which the J2EE-specific solutions are implemented. Constructing this foundation correctly widely affects the entire architecture's robustness and extensibility. If not constructed correctly, the foundation would minimize the architecture's usefulness regardless of how many J2EE problems it solves.
Don't make a checklist covering the software development patterns your architecture requires, as you would with the J2EE patterns. Instead, employ such patterns where appropriate based on your project's specific challenges. Many developers mistakenly believe that their products will improve if they use more patterns—or if they use all of them! That, however, is not the case. Use discretion and finesse when deciding which patterns to employ and how to use them together.
Design patterns: Where is the code?
Keep in mind that design patterns do not come with the exact implementation, or source code, you will use. Design pattern offerings range from sparse textual descriptions to rich documentation to possibly some sample code. The challenge comes in applying the patterns' powerful ideas. These ideas must be applied to the environment in which they will be used; the environment defines the correct implementation.
As an analogy, consider a design pattern for building a house's foundation. The design pattern identifies the problem, the context, and the possible solution for constructing the foundation—information immensely valuable to the construction worker in the field. The worker must still, however, build the foundation. Wouldn't that construction worker benefit more from being given the foundation (similar to the software developer being given the implementation)? Maybe this foundation would just be a slab of concrete that the house could be built on. The problem: The foundation must integrate with the house itself and the land where the house will reside. How can such a prebuilt foundation accommodate all the possible house floor plans (rectangle, square, and other odd shapes) and all the possible landscapes (on top of a hill, in the middle of a forest, and so on)?
Back in the software world, the feasibility of using prebuilt design patterns hinges on two factors:
- The implementation, not individual design patterns, represents a solution. The solution could incorporate multiple design patterns, and, in doing so, would know how the individual design patterns play together.
- The solution must be adaptable, which answers the final question from the analogy of the prebuilt foundation: the foundation must be able to adapt to the terrain and the floor plans. As you can imagine, it would take an extremely skilled artisan to build the adaptable foundation as opposed to the standard foundation.
Common design patterns
The table below lists some common design patterns from both J2EE sources and broader OO patterns.
Let's look at two J2EE design pattern examples: the Session Facade and Value Object patterns. Both demonstrate how J2EE design patterns focus on problems particular to the J2EE environment, as opposed to the software development design patterns that generally apply to any application development effort.
Example: The Session Facade J2EE pattern
The Session Facade pattern evolved from experiences with Enterprise JavaBeans (EJBs). Systems built on the newly introduced entity EJBs (which communicate with a database) were slowing to a crawl. Performance testing revealed problems stemming from multiple network calls made when communicating with the entity EJBs, which added overhead for establishing the network connection, serializing the data for both sending and receiving, and other effects.
In response, the Session Facade pattern improved performance by centralizing those multiple network hits into a single call. Session Facade employs a stateless session EJB to mediate between the client call and the required entity EJB interaction. More patterns exist for improving database access performance, including the Fast Lane Reader and Data Access Object patterns.
Example: The Value Object J2EE pattern
The Value Object J2EE pattern also aims to improve the performance of systems that use EJBs over the network. Those overhead-inducing network calls from the previous example retrieve individual data fields. For instance, you may have a
Person entity EJB with methods such as
getLastName(). With the Value Object design pattern, you can reduce such multiple network calls to a single call with a method on the entity EJB, such as
getPersonValueObject(), that returns the data all at once. That value object contains the data that the entity EJB represents and can be accessed as needed without incurring the network call overhead.
Example: The Flyweight OO pattern
For an example of a broadly applicable OO design pattern, consider the Flyweight pattern, which improves application performance through object reuse. OO software produces overhead—wasted CPU cycles, garbage collection, and memory allocation—when it creates and destroys object. If the system could reuse those objects, you could avoid that overhead. The objects often aren't reusable, however, because they contain information (called state) specific to the object's current user. The Flyweight pattern provides approaches for moving that state elsewhere so the rest of the object can be reused.
Put them all together: Persistence example
Now that you know the basics, you can begin applying design patterns in your development practices. But how do you actually use patterns? Begin by identifying a domain or technical problem requiring a solution. Persistence—solving the age-old object-to-relational database mismatch—represents a good example for most enterprise applications. Let's see the steps required to design and build an application architecture's persistence layer.
Following the traditional OO architecture and design approach, create use cases describing your persistence needs. Possible use cases include:
- Object persistence should be transparent from the developers' viewpoint.
- Persistence mechanisms—entity EJBs, Data Access Objects, and so on—should be configurable at the architectural level.
- Our architecture should utilize J2EE technologies but encapsulate J2EE dependencies. We should be able to change J2EE application server vendors, J2EE versions, or replace J2EE completely without requiring an entire application overhaul.
- The resulting persistence layer should be reusable across projects. This should be part of our ongoing application architecture.
Once you have identified the problem, you can decide which patterns apply. Remember that for the J2EE patterns, you should determine which patterns apply in the problem area and address them. For persistence, the relevant J2EE design patterns are (see Sun's J2EE design pattern books in Resources):
- Value Object
- Fast Lane Reader
- Data Access Object
- Session Facade
- Composite Entity
- Value List Handler
Since you'll employ EJBs, include the Business Delegate and Service Locator patterns to address EJB access.
Additionally, solving the second and third use cases requires traditional software development design patterns. How do you encapsulate dependencies and have configurable persistence mechanisms? Some applicable software development patterns include: