July 28, 2011, 1:15 a.m.
posted by franni
What Is a Layered Architecture?
What is a layered architecture, and what does layering mean? Application layering is the separation of architectural concerns whose boundaries are supported by interface contracts. Typically, these layers are stacked vertically so that each layer interacts only with the layer directly underneath it (Figure).
Because an interface contract exists between each layer, changes can be affected on Layer 3 with minimal side effects on Layer 1. Moreover, Layer 3 can be totally replaced, as long as it meets Layer 2's contract, without affecting Layer 1. This property is known as strict layering.
As mentioned earlier, the principle of layering is core to J2EE and WebSphere. From the J2EE perspective, this concept has been the driving force behind much of the standards work that defines the platform. This can be seen in how presentation and data access are designed. The WebSphere Application Server fully leverages this architecture by allowing individual layers to be scaled and distributed independently.
Critics of strictly layered architectures argue that performance, and sometimes extensibility, are sacrificed since more activity is required to propagate down through the layers. Extensibility can suffer if contracts defined between the layers are not robust enough to handle future requirements. However, being able to strategically distribute the application layers, use the domain layer across multiple applications, and easily configure different data sources and user interfaces overcomes such criticism.
Nonstrict layering (Figure) allows higher layers to access any layer defined below it and answers the critics' arguments against performance and extendibility; however, it nullifies the benefits of strict layering.
1 Common layering schemes
Traditional two-tier client/server-based applications can be partitioned into two layers—presentation and data access. A two-tier Graphical User Interface (GUI) application would simply query a data source, compute, and display the information to the user. A consequence of this simple architecture is that knowledge about the business domain is both scattered throughout the user interface, and is forced into complex database schemas.
Object technology encourages not only the abstraction and reuse of presentation logic, but also business processes and data. Therefore, decoupling application logic from application presentation results in scalable three-tier distributed systems that allows objects defined to model business data and processes to be used across application boundaries. With the explosion of the Internet and related technologies, enterprise application requirements have made the existence of layered application architecture imperative.
The most common layers of an application can be partitioned into presentation, domain, and data source sections (Figure). The most important layer is the domain. This is where business process and state are captured. Presentation layer objects either consume or exercise domain objects. Data source objects defined in the data source layer access specific data sources on behalf of domain objects requesting or saving state.
It is not enough to merely stipulate the layers in a graphic and expect developers to properly partition application elements into a layered architecture. Developers must implement functionality within each layer in a consistent fashion. Moreover, message interaction between layers must be formalized.
Formalizing layer interaction should involve a decoupled design that includes appropriate indirection in support of layer substitution. Additionally, behaviors prescribed across all applications, namely exception handling, logging, start up, and shutdown operations, should be formalized and consistently applied.
2 Layered Architecture Definition
The primary motivation for layering is to create and preserve a reusable domain model that spans application boundaries. Other advantages to this architecture are that it helps organize the project and allows construction and validation of each layer to vary independently. Of course this can be accomplished with three layers; however, introducing two additional layers between presentation and data source layers further decouples the domain from application presentation and data source requirements (Figure). An example of layer substitution would be enabling the domain layer for pervasive devices or a voice response unit. In Figure, this would imply creating only the new presentation layer constructs and the necessary mediators to interact with the existing domain model.
Let's take a closer look at each of the roles and responsibilities of the layers in this five-layer architecture.
The presentation layer consists of objects defined to accept user input and display application outputs. The most common presentation technologies that can be used with J2EE are:
We will discuss the relative merits and proper uses of these technologies in Chapter 5 and revisit them with regard to deployment issues in Chapter 22. Chapters 5–15 describe the technologies used in developing presentation and control layers in J2EE.
2.2 Controllers and Mediators
Because a primary goal of layered applications is to enable domain logic to be reused in different presentations, how the user interacts with the business model needs to be isolated. This is the role of the controller. Whatever the presentation technology happens to be, requests for domain state and behavior will be done via a controller object defined for the particular presentation requirements; e.g., HTML, Swing, or pervasive devices. This controller object implements the mediator design pattern from [Gamma]. An important design requisite involves making sure that domain-specific logic is not defined in presentation object methods, but rather, is obtained from a mediator referenced domain object. Additionally, application navigation topology is defined within this layer.
Application presentation objects interact with a domain model in generalized ways, regardless of the presentation technology. For instance, a GUI will present a list of choices which are composed of a collection of domain model objects; the same collection can be used to populate an HTML list. For that matter, the same collection could be used to provide a list of choices in a voice response unit interface (Figure).
Mediators capture and decouple application-specific functionality from presentation technology by performing domain model requests for presentation or controller objects that drive a specific application use case. Mediator classes are defined to satisfy a specific application user interface function or use case; therefore, they are less granular than controllers. For example, a single mediator can be used to implement a user registration function with a wizard-like interface. Mediators implement behavior that would usually end up in presentation classes as methods/scripts. Moreover, consistently applying mediator objects offers more than loose coupling between a domain model and presentation technologies. Mediators provide a convenient and consistent way to transfer application state between user interfaces, eliminating the typical highly parameterized approach. Additionally, transaction behavior finds an appropriate location in mediator objects since navigation and units of work constraints are tied to application-specific functionality.
The key to mediators' presentation independence is the enforcement of strict layering, meaning that mediators should not contain any references to presentation objects. However, they are free to reference domain-object public state and behavior. Care must also be taken not to define domain-logic in mediators. This pitfall can be avoided by applying a simple experiment. Ask yourself, "Can I still perform or obtain the requested domain operation using only existing domain objects?" If the answer is "No, I need a mediator object," then the mediator is implementing behavior that belongs in the domain.
The domain layer is the hardest part of a layered system to understand, and the most challenging to implement. To understand what a domain object is, you have to go back to the basic roots of object-oriented (OO) programming. When we learn Java programming or OO, design the first examples seen are usually in terms of concrete objects. This might be an example of a control system like in [Booch] where the objects modeled are physical like TemperatureSensors and AirConditioners, or it might be through a simple game where objects like playing cards are modeled.
Unfortunately, when many programmers start looking at their own day-to-day problems they instead see more abstract things like windows and database tables, not the nice, concrete things seen in the books and tutorials. This is unfortunate, since modeling the aspects of a business in software can be one of the most powerful tools that a programmer can bring to bear on solving the hardest problems in software development. Capturing business abstractions in objects can make a system much more powerful by making it more flexible and can create a critical distinction between the parts of a system that represent the business problem being solved (its essence) and the accidents of implementation resulting from choices in technology that might be transitory.
Domain objects are usually implemented as standard Java classes or plain old Java objects (POJOs). J2EE provides another option for implementing domain objects that we will examine more closely in later chapters. A programmer can choose to implement his domain objects as Enterprise JavaBeans (EJBs), which conveys some benefits in terms of distribution, transaction capabilities, and persistence. Even when EJBs are used, a mix of standard Java classes and EJBs should be employed, as we will examine in greater detail in Chapters 19 and 30.
A consequence of building a domain layer, as we have described, is that it should not be concerned with purely implementation-specific details. For instance, one of the most common questions in enterprise programming is how to extract data from or update data in a database. Rather than making this behavior part of the domain object, a second set of objects is required to perform this function. Separating the behavior in this way conveys a number of benefits, including making it possible to change implementation details like database vendor or database schema without changing the domain implementation.
A design like this requires a separate layer, often called mapping or persistence, that can move data from domain objects to back-end data sources and vice versa. There are several open source and commercial products, like Apache Castor, CrossLogic's Universe, and Oracle's TopLink that can add this behavior. However, for a programmer using J2EE, the common way that this behavior will be used is through the APIs provided by the EJB container. As we will describe in Chapters 19 and 23, the EJB container in WebSphere provides a simple and consistent interface for data persistence using entity beans. However, we may still need to implement some mapping functions even in designs using EJBs when we need to move data between JavaBeans and EJBs. We will cover this topic in-depth in a later chapter.
3 Data Source Access
At some point in your application, you have to retrieve and store data, or communicate with external systems. Undoubtedly relational databases (DB2, Oracle, Informix, etc.) are the most common way IT organizations store and query enterprise data. Recognizing this profound market share, Sun delivered the JDBC (Java Database Connectivity) API. JDBC allows the production and execution of vendor-neutral Structured Query Language (SQL). Developers can use standard ANSI SQL against any JDBC-compliant driver. The specific API and types of available JDBC drivers are beyond the scope of this discussion; refer to the JDBC specification, available on Sun's Java Web site (http://www.java.com), for more information.
However, there are other common data access mechanisms in J2EE as well. Enterprise information systems (EIS) such as CICS, SIEBEL, SAP, or J.D. Edward's OneWorld are a common part of today's enterprise landscape.
J2EE offers two ways to connect to EIS: Java Message Service (JMS) and the Java2 Connector Architecture (J2C). JMS provides asynchronous access to corporate data, while J2C provides synchronous access to these EIS systems.
Finally, you may access external systems through Web Services, which is the newest mechanism for providing open access to enterprise services. We'll examine JMS in Chapter 27 and we'll cover Web Services in Chapters 32 to 34. We will not cover J2C in-depth in this book. However, vertical layers are not enough to build and deliver complex enterprise applications. Application layers must be complemented with application services and test facilities.
4 Application Services
There are application responsibilities that developers must apply to all application development efforts. Implementing these activities consistently using a design that is extendable will facilitate reuse and minimize side effects when requirements change. Moreover, standardizing these services across all applications can yield efficiencies in determining and communicating new development and in maintenance activities.
Obvious application responsibilities include error handling, status tracing, application start up and shutdown, accessing externalized properties, and applying preferences. A design must be put in place that not only allows developers to consistently apply error handling across all applications, but also supports the ability to install and change these behaviors on an application basis. For example, application status is sometimes reported to a console. What happens if the application is server-based and a console does not exist? A design should allow error logging and tracing to be routed to a flat file, perhaps in addition to the console.
5 The Virtues of Test Scripts
The primary design intent of a layered architecture is decoupling a problem space domain model from presentation and data source requirements. Reuse of the domain, at least across application boundaries, can be achieved if isolation is accomplished.
Creating test scripts that exercise domain model behavior helps to verify domain isolation and provides other significant benefits. In fact, test scripts should be developed for all the different layers of your application. We will cover this in Chapter 17.
Figure provides the high-level picture of an enterprise application topology where vertical layers are supported by application services and test frameworks.