Architecting and Designing Scalable, Multitier Systems
There are several techniques for architecting and designing multitier systems so that they are flexible and scalable. The key to flexible and scalable systems is distributed processing, and a key to distributed processing is code mobility. Achieving these goals is the focus of the following design techniques: the Flexible Model View Controller (MVC) Technique, the Categorizing Business Rules Technique, and the Domain Bean Mobility Technique.
Multitier Systems Overview
The typical tiers in multitier systems are the front-end tier, middle tier, and back-end tier as shown in Figure 1.
Figure 1. Typical multitier systems.
The front-end tier resides on end users' computers/devices and displays the interface the end users interact with. For the same system, the front-end tier can be different types of user interfaces (UI) running on various types of platforms. For example, the front-end tier for Internet users are typically browsers with or without applets running on end users' computers. To provide a rich and interactive interface for intranet users, the front-end tier could be Java clients using Java Foundation Classes (JFC) in addition to the browsers. For wireless users, the front-end tier could be Wireless Markup Language (WML) and J2ME running on various wireless devices such as mobile phones.
The middle tier consists of the Web servers and application servers that serve up static content and dynamic content, respectively. The middle tier could be broken up into multiple middle tiers to separate not only the Web servers and applications, but different types of application servers as well. This topic is beyond the scope of this article.
The back-end tier is the database used to store persistent data. I address only the front-end tier and middle tier.
The Flexible Model View Controller Technique
This design technique involves designing Model View Controller (MVC) in an unconventional way for the Web environment. The typical MVC technique for the Web environment is shown in Figure 2.
Figure 2. Typical MVC technique.
The browser requests are sent to the servlets, which contain the controller logic to process the data in the browser requests and access/set up the beans as needed. The servlets then forward the requests to the Java Server Pages (JSP), which then use the beans to display the Web pages (see Listing 1).
The problem with this approach is that controller logic is embedded in the servlets, so other UI types, such as JFC clients, cannot reuse the controller logic. The idea behind the controllers is that they contain control logic that coordinates business rules and then typically takes some input data, processes it, and returns some output data or condition code. This processing logic is independent of the UI and where the input data comes from. Therefore, to reuse the controller logic for different types of UI, it needs to be separated from the servlets and put into regular Java classes. The UI components for different UI types are responsible for converting the input data to necessary Java data types and/or beans as required by the controllers' method signatures and invoking the same controller methods. With this design technique, you achieve the flexibility shown in Figure 3. In this figure, different flows for various types of UI are indicated as 1A, 1B, etc. Step 3 is shared among Flows 3A, 3B, and 3D.
Figure 3. Flexible MVC technique for more flexibility and scalability.
For the HTML UI, the flow starts as 1A. In this case, the servlets' role is limited to converting the input data in HTML forms and URLs to Java data types and/or beans and invoking the controllers' methods to process the data. Based on the return conditions or output data of the controller methods, the servlets can then forward the request to different JSPs. The JSPs can also invoke the controller methods to obtain data/beans for display purposes (see Listings 2 and 3).
Using this approach, when the system needs to support other types of UI, you simply provide the interface component that is unique to that UI and reuse the same controller logic. This means less code development. For JFC clients, you have two options of using the controller; these are shown as Flows 1B and 1C in Figure 3. With the first option (Flow 1B), the controller is left on the middle tier and a thin Remote Method Invocation (RMI) wrapper is provided to it, so the JFC clients can invoke the controller remotely. With the second option (Flow 1C), the controller is packaged with the JFC clients in the same program. This can be done because the controller is implemented as regular Java classes to begin with. Option 1C is more scalable than Option 1B because the processing of the controller logic is spread out among the users' machines as the number of users increases. An extension to Option 1C is to move some of the beans to the front-end tier. This extension is discussed in detail in the "Domain Bean Mobility Technique" section found later in this article.
For wireless devices, the flow is similar to the browser flow in that you have view processing components that interact with wireless devices and reuse the same controller. For wireless interfaces using WML, even if you use a Wireless Access Protocol (WAP) Gateway to interface with the servlets, one HTML input screen may be implemented as multiple WML screens because of the wireless device's small screen size. Therefore, the same servlets cannot be used to process both HTML and WML interfaces. With the typical MVC technique, you therefore need to duplicate the controller logic in multiple servlets. With the Flexible MVC technique, you simply provide different servlets for HTML and WML and invoke the same controller.
By separating the controller logic from the servlets, you can obtain the following advantages:
- A more flexible system. As the system requires a new UI type, you just provide the interface component for that UI type to be able to reuse the controller logic.
- Code mobility. Because the controllers are designed as regular Java classes rather than servlets, you can deploy the controllers on different tiers as needed.
- A more scalable system. By packaging the controllers with the UI code as needed, such as JFC clients, they are executed on end users' computers. This is achieved via code mobility described in the previous bullet. Note that you have the choice of doing this for individual controllers, so some controllers can stay on the middle tier and some can be duplicated and packaged on the front-end tier. This decision is based on the nature of individual controllers.
The Categorizing Business Rules Technique
While various books and articles mention business rules, they do not differentiate between the different types of business rules. With the advent of Enterprise JavaBeans (EJBs), you need to make the following design decisions regarding the design for business rules: EJBs vs. regular beans; whether to use session beans or entity beans for EJBs; and whether to provide business logic, database persistence code, or both for EJB entity beans.
The key design issue is determining which components to provide what business rules. The Categorizing Business Rules Technique helps you address such design issues.
There are at least two types of business rules: domain layer rules and dynamic/transactional rules.
Domain layer rules pertain to the attributes of the domain objects and the relationship among the domain objects. An example of a domain layer business rule would be that the credit card number entered by users needs to be a 16-digit number, or that an e-mail address is required and needs to conform to a certain format. An example of a domain layer rule that deals with the relationship among domain objects is when each user can have one to three addresses (work, home, and mailing). In this case, you may have multiple Address objects associated with a User object, but a persisted User object is valid only if it contains one to three Address objects of different address types.
Dynamic/transactional rules pertain to business rules such as the verification of the data itself, rather than its format and the requirement that certain steps be performed as a single transaction. An example would be that the 16-digit credit card number entered by users needs to be verified as a valid account. An example of transactional rules (if any one step fails, none of the other steps are performed) would be verifying the credit card number; checking to see if the credit card account is authorized for the charge amount; and placing the order.
As you can see from the credit card example, there are two distinct types of business rules. The domain layer rule requires the credit card number to be a 16-digit number, and the dynamic/transactional business rule requires the credit card number to be verified and authorized for the charge amount. These rules need to be distinguished and implemented in different components so the credit card number can be checked for its format once and persisted to save time for the user's subsequent orders. On the other hand, the dynamic/transactional rules need to be processed each time an order is placed because the credit card account may be closed or the charge amount may not be allowed at the time of the order.
Distinguishing between these business rules helps you determine what type of beans to place the business rules in. EJB entity beans and regular beans should be used to implement the domain layer rules because they represent the domain objects, their attributes, and their relationships. For dynamic/transactional rules, session beans and controller logic should be used because they can provide the dynamic/transactions operations needed. In the order placement example described above, you would use a session bean to implement the steps described.
The next step is determining whether to use EJB entity beans or regular beans for domain layer rules. For multitier systems, a key factor in this design issue is determining the different tiers you plan to deploy the business rules on for better system scalability and flexibility. Typically, domain layer rules, such as the format of the credit card number, do not change frequently. On the other hand, dynamic/transaction rules are more likely to change. Therefore, domain layer rules are good candidates for placement on the front-end tier so that they can be enforced as soon as possible, whereas dynamic/transaction rules are good candidates for placement on the middle tiers.
Making the right design decision in this area is a critical step in maximizing the advantages of multitier systems by using different tiers to share and spread the load among different application logic. In fact, the Domain Bean Mobility technique addresses this issue. By recognizing the differences between domain layer rules and dynamic/transactional rules, you will have a guide as to what business rules/code can be placed on the different tiers. In particular, certain domain layer rules can be placed on either the front-end or middle tier.
The Domain Bean Mobility Technique
A typical design trend for multitier systems uses a thin client with a fat server. In this approach, all the business rules are deployed onto the middle tier and the front-end tier provides only the UI. An example would be a system that requires users to enter personal data before entering.
In the pure, thin-client approach, the registration screen on the front-end tier contains just HTML and all the data validation is performed in the middle tier, which could be done by either servlets or beans. If servlets are used to validate the user data, however, the system is vulnerable and flawed. The reason? When beans are used without servletsas in Flows 1B through 1D in Figure 3the user data is not validated. If beans are used to validate the user data, the system incurs a significant overhead for invalid user data because the servlets need to:
- Create the controller and beans;
- Store the user data into the beans to validate them;
- Find out the user data is invalid;
- Delete the beans; and then
- Dynamically regenerate the registration page to indicate fields with invalid data.
In addition to these deficiencies, there are serious drawbacks to system scalability because more network traffic and server load is required (see Figure 4). To keep Figure 4 simple and to point out the extra network traffic between the front-end tier and middle tier, servlets, JSP, controllers, and beans are shown as a single component under "Middle Tier." However, their interactions are clear from the flow descriptions.
In this sample processing flow there are seven network traffic flows between the front-end tier and middle tier due to errors in the user data. In the Internet environment, the extra network traffic can have a major impact on performance due to such factors as:
- Potentially slow connection speed from users' computers to the Internet.
- Accumulative effect of extra network traffic by multiple users of this system into the system servers.
- Overall network traffic load across the Internet at the time users access the system.
In addition to the extra network traffic, the middle tier needs to perform three times the number of processing events to validate the data and register users. As the number of users increases, the accumulative effect of extra network traffic and extra processing on the middle tier will significantly impact system performance and scalability.
- The beans are not implemented with the domain layer rules to validate the data. This makes the system vulnerable and flawed because data from processing flows that do not go through the Web pagesas in Flows 1B through 1D in Figure 3are not validated.
To resolve the issues of thin-client approaches and to make the systems more scalable and better performing, this article presents the "Domain Bean Mobility Technique." Rather than using thin clients and fat servers, this design technique focuses on designing "smart" servers and "smart" clients. With this design, certain domain layer rules are placed on the front-end tier as needed to realize the benefits of distributed processing. To do this, you implement corresponding domain layer rules as regular beans rather than EJB entity beans. This will give you code mobility because the regular beans can be deployed either on the front-end tier, middle tier, or both (see Figure 5).
Note that all the extra processing for validation of user data is done on the front-end tier, so there are only three network flows between the front-end tier and middle tier compared to seven network flows in the thin-client approach. Also, the middle tier handles only one round of processing for user registration vs. three for the thin-client approach. With less network traffic and load on the middle tier, the systems using the Domain Bean Mobility Technique are much more scalable. In addition, this design provides users with much better response time because user errors are processed on the front-end tier. Other advantages with this design technique are:
- Code mobility. Users can deploy the domain business rules on different tiers as needed.
- Because the same regular beans can be used with different client code, such as applets, JFC client program, and controller logic, there is more code reuse.
- Because the domain layer rules are implemented in the beans, all the processing flows that use the beans are constrained by the same business rules regardless of whether they go through the Web pages or not.
- Better performance and scalable systems because:
- Data validation is done as early as possible on the front-end tier, and users see less latency.
- There is less network traffic between the front-end tier and middle tier, since only valid user data is sent back to the server.
- The servers on the middle tier do not handle invalid data requests and therefore provide faster processing for valid data requests.
- As the number of users increases, the workload of validating domain layer rules is spread among the end users' computers at the front-end tiers, and the network traffic is limited to valid user data only.
- When domain layer rules are violated, the beans can generate application-specific exceptions to provide centralized and consistent error messages to users, regardless of the types of interfaces.
- Lower overall costs for the system because fewer servers are needed to support the same number of users as required by the thin-client design. The cost of additional servers involves not just the hardware, but the application server software, which typically ranges in price from $10,000$20,000 per CPU. For large-scale systems with thousands of users, the costs of additional hardware and software add up quickly.
The disadvantages of this technique are as follows:
- Depending on the HTML pages, non-UI applets in Netscape 4.7x browsers may affect the layout of the pages, particularly when the applets are placed in the middle of the page. If you encounter this problem, you need to move the applets to another place in the page, typically at the beginning or end of the page. Since the non-UI applets are not displayed on the page, their placement in the HTML page does not affect their functionality.
- Java beans are restricted to Java Version 1.1 because IE 4.x/5.x and Netscape 4.7x support that version. Since the beans are non-UI and contain just business rules, their dependencies on Java Version 1.1 are minimal. Further, from my experience, the differences between the browsers' Java implementations are related to UI code rather than non-UI code.
- The fresh installation of IE 5.x does not include the JVM by default. Users can still have the option to include the JVM in the installation. Without the JVM, the first time IE 5.x encounters an applet in a HTML page, it prompts the user to download the JVM. This is a one-time operation. You can either use this approach or fall back to the thin- client approach for IE 5.x by having another version of HTML without the applet. With the Domain Bean Mobility technique, you can handle both thin and smart clients by deploying the regular beans on both the front-end tier and middle tier. This is achieved by the code mobility technique used for the domain layer beans, and therefore, your system is flexible.
With the design techniques described in this article, you can design your code so that it is mobile and can be deployed on different tiers. By being able to deploy code on different tiers, you are able to architect and design multitier systems that actually use the different tiers for distributed processing. As shown, this results in systems that are more flexible in handling different UI types and more scalable as the number of users increases.