Overview of multi-tier design

Typical platform-dependent development leaves the internal details of the particular platform up to the application developers to worry about. Areas such as object-activation, type-systems, protocols, platform-standards, etc. are often re-implemented with each new development effort. Platform-dependent information is often structured in a near-sighted format which can only be interpreted with specific platform tools and technologies. A multi-tier, platform-dependent programming model must be able to communicate and exchange information between system demarcations in order to fulfill business needs.

Multi-tier application development is beginning to see standards and best practices finally reach a point to where developers can rely on certain features and functionality existing no matter the infrastructure vendor. In this article, we will discuss some design techniques that can be employed in a standard multi-tier framework (shown in figure 1), that are useful for building flexible and scalable services and applications.

Figure 1: A typical multi-tier application framework

Decoupling presentation, user interaction, and state model using MVC

Multi-tier application interactions usually consist of an HTTP-based request passed from a client to an HTTP server where business-domain logic is executed. The response from the business-logic object is then formatted as some type of markup language (HTML, WML, XML, etc.) and passed back to the client. This interaction is often embodied within the model-view-controller (MVC) pattern.

Using the MVC pattern to encapsulate client/server interactions helps to illustrate software-pattern roles as well as developer roles by separating object, components, and services into tiers with well-defined boundaries. Other benefits derived from this pattern include the ability to easily maintain, manage, and extend the system; more easily support multiple client devices; simplify testing procedures; and reduce code duplication.

The MVC pattern as it is applied to multi-tier applications is illustrated in figure 2.

Directing application flow using XML state maps

Defining the control-flow for each application is a time-consuming endeavor. Without a standard framework to work with, each application can end up sharing very little of its control-flow logic with another application. This leads to buggier software and maintainability nightmares.
An entire application's flow, business rules, exception handling, authorization rules, allowable executions per state, and views per state can be declared in an XML-based state map.

State maps facilitate declarative construction of request-handlers and views for each application state. This promotes consistent behavior for each client request and response. Adhering to a state-map restrained execution model helps to keep an application's model and views in sync with each other. Each transition from state-to-state always takes place within the confines of a controlled set of requests and request handlers.

Transitioning in a state-to-state manner using requests from randomly selected URLs presents a high-degree of complexity to multi-tier developers. It is impractical to suggest that the actions of a Web-browser user can be predicted in any sort of consistent manner. In a multi-tier application, a view is displayed and then the user is typically presented with a number of different choices that can be made and selected in order to navigate to the next desired state. This freedom of choice is an advantage to users, but requires careful and creative solutions by application designers and developers.

In a state-driven, multi-tier application, a request can be processed by a front controller. The request is then handed to a request processor, which retrieves the name of the desired action or view from the request data. The request processor then looks at the current state and determines if the application is allowed to make the transition from the current state to the next state. If the transition is allowed, the front controller is notified as such and the state information, along with the request data, is given to the view-flow manager. The view-flow manager then retrieves the name of the next view and hands it to the transform engine to process. This interaction is illustrated in figure 3.

Figure 3: State transition interactions using a declarative state table

Well-designed presentation frameworks separate view information from business-domain code in declarative state maps. This can facilitate a layer of abstraction within a multi-tier-tier framework by converting logical view names to physical objects, and state-mappings to application flow. This layer of abstraction allows business experts to alter the application flow and view-names without reprogramming. Within the presentation framework, a front controller can use the state mappings and view names to route HTTP requests to business domain services, views or additional request handlers.

Within the Spring framework, you use handler mappings to map incoming web requests to appropriate handlers. When a request is received by the Spring framework, the Spring DispatcherServlet will hand it over to a handler mapping to be resolved. The handler mapping inspects the request and decides on an appropriate HandlerExecutionChain object. When finished, the DispatcherServlet will execute the handler and any interceptors in the chain.

The following example illustrates a handler mapping with one interceptor that intercepts all requests and reroutes the user to a specific page if the time is not between 9 a.m. and 5 p.m.

Using servlet filters to provide uniform services for all requests

Typically a business requires certain tasks such as security and logging to execute for each incoming request and/or outgoing response. Servlet filters may be placed ahead of a front controller servlet to add functionality that will be applied to all incoming requests and/or response. Servlet filters intercept requests before they reach the front controller.

Servlet filters can be linked together in a chain allowing a multiple global services to be added to each incoming request. The interaction of requests applied to servlet filters can be seen in figure 4.

Figure 4: HTTP requests applied against servlet filters

Servlet filters are Java classes that implement the javax.servlet.Filter interface. The javax.servlet.Filter interface defines three methods:

The doFilter() method is the place where each filter's operations are performed.

Reducing complex interactions between tiers using the Facade pattern

Decoupling tiers within a distributed framework allows for unplanned scaling of the framework, clustering, etc. However, decoupling requires a protocol-demarcation component that will locate the appropriate target objects and services and route requests to them.

The Facade design pattern provides an effective protocol-demarcation by grouping multiple fine-grained objects behind a single interface to reduce traffic across distributed tiers. This encapsulation of responsibility enforces reusable design and implementation and reduces network overhead. It also enables a centralized location for security management, traffic monitoring and transaction control. In a service-oriented architecture (SOA), coarse-grained abstraction of business-domain logic can be accomplished by applying the Façade pattern to existing business-logic classes.

The interactions of the Façade pattern, embodied within a business-service facade, are illustrated in figure 5.

Figure 5: The Façade pattern embodied within a business process controller

In a multi-tier J2EE environment, stateless session beans are often used to implement the Façade pattern.

Handling asynchronous messaging with Message-Driven Beans

An increasingly important function of multi-tier application platforms is their ability to handle asynchronous messages passed from message-oriented middleware (MoM) systems. Within the Java environment, the Java Message Service (JMS) and message-driven beans are provided to handle publish/subscribe messages.

Message-driven beans (MDBs) are stateless, transaction-aware enterprise components that are designed for processing asynchronous JMS messages. Message-driven beans receive JMS messages from their container within callback methods that are to be implemented by a business-domain developer.
MDBs consume and process messages concurrently in a thread-safe environment, freeing the developer from most multithreaded worries.

An MDB is an enterprise Java bean, with a few differences. An MDB has a bean class and XML deployment descriptor, just like a session bean or an entity bean, but, since the MDB is not callable from traditional clients, it does not have a component interface.

Figure 6 illustrates a typical way to integrate MDBs into the business tier of distributed applications.

Figure 6: Message-driven beans interacting in the business tier

The MDB's onMessage callback-method is used to implement any business-domain code.

Eliminating proprietary data-access dependencies with the DAO pattern

The Data Access Object (DAO) pattern provides an abstraction layer between the business tier and the enterprise information tier. Business-domain services residing in the business tier access data resources residing in the enterprise information tier using objects and components which implement a DAO interface. This layer of abstraction hides the implementation details of each specific information-source/data-store. Thus, changes made to each information-source/data-store will not require reprogramming of the business-tier components. The DAO implementation classes are the only objects that need to change. Figure 7 illustrates a typical DAO class and interface design.

Figure 7: A typical DAO class diagram

A generic DAO interface is illustrated below that exposes methods which embody the CRUD (create, retrieve, update, and delete) design pattern. The parameters for the DAO methods are specific to each type of DAO.

Spring's IoC support, along with its abstraction of transactions and exceptions allows you to easily swap DAOs in-and-out and to isolate all datasource-specific code in the enterprise in the integration tier.

Spring also provides a set of abstract DAO classes that you can extend, providing methods for setting the data source and any other configuration settings that are specific to the storage technology you wish to use. These DAO-support classes are:

JdbcDaoSupport - super class for JDBC data access objects.

HibernateDaoSupport - super class for Hibernate data access objects.

JdoDaoSupport - super class for JDO data access objects.

Using XML and XSLT to enable multi-client presentations

XML has become the de facto standard for data transfer of all types of applications. Another powerful trend that has taken place is the use of XSLT to transform XML content to comply with the needs of a given target. One such use of this data transformation is the conversion of XML data to presentation markup such as HTML, WML, etc. Using XSLT to convert data to match a client device allows developers to make the formatting decision late in the rendering process. This enables multiple client devices to be supported from the same data model.

XSLT is a tag-based language that is used to transform XML into other forms of markup, such as WML or HTML. The process of transforming XML data using XSLT is illustrated in figure 8.

Figure 8: Data-model formatting using XML and XSL

Xalan is an open source XSLT parser that can be used to convert XML into HTML, WML or other markup.

Below is example code that reads in an XML file, applies an XSLT file, and returns an HTML document as the output. Important to note are the facts that the servlet's PrintWriter and the XML and XSLT files are passed as parameters.

Spring provides the AbstractXsltView class which is the superclass for views rendered using an XSLT stylesheet. Subclasses must provide the XML document to transform. They do not need to concern themselves with XSLT.

Using XML as the common data format

The spread of XML has reached into a number of different areas that data management and formatting tools and technologies have not gone before. This saturation-of-use makes XML a technology that is capable of providing a standard, scalable, flexible and manageable data model. XML provides a data model that is supported by most data-handling tools and vendors. Structuring data as XML allows hierarchical, graph-based representations of the data to be presented to tools, which opens up a host of possibilities. Data once thought of as unstructured can now take on a new personality as structured, context-based information. By using XML to define a common object structure, data structures can be used by any enterprise information tool that supports XML.

XML's flexibility allows data models to be updated or enhanced without reprogramming the objects that use them. When new fields are added, the only code modifications that need to be made are to the objects that understand the new fields. Common abstraction design tactics can also eliminate these code changes.

XML parsing is a late-binding technique that abstracts physical location of data from the code that reads it. This technique assures that data consumers make no assumptions about data location, creating a robust system of data transfer. Figure 9 illustrates the interactions that take place in a typical conversion of XML from one form to another.

Figure 9: Data formatting using XML and XSL

Java provides several different ways to use XML including reading XML from a file, transferring XML as a JMS message, transferring XML from component-to-component and responding to client devices from servlets as XML.

Xalan and xerces are two of the more popular XML/XSLT technologies. Both are included with the most application servers and frameworks. They can also be downloaded from Apache's Website, if needed.

Along with the AbstractXSLTView class mentioned above, Spring also provides the format helper class which provides Xalan extension functions such as date and currency formatting.

Conclusion

Multi-tier application development has evolved to a point where standards and best practices are employed by most application server vendors. This consistency is allowing developers to rely on certain features and functionality existing no matter the infrastructure vendor. Using the design and implementation tips mentioned in this article, within a framework of standards and best practices, allows you to build flexible and scalable multi-tier services and applications.

For Additional Information

For more information about the technologies discussed in this Cool Solution for Developers, refer to the following resources: