We all know that the concept behind client/server is to split the processing among two or more network-connected computers. In fact, we have a name for this concept: application partitioning, or the process of dividing an application among available servers and clients. Although this architectural concept is simple, most client/server application architects and developers don't understand the importance of a sound application partitioning scheme, or they don't have an effective process to create one. This fundamental error kills many development projects. I'll explain why.
Simply put, application partitioning is the process of splitting application processing -- it is the essence of effective client/server design. There are application partitioning tools that help developers move objects and application components dynamically from one computer to the next. In many instances, however, application objects are native to a particular machine and can't move after deployment. Such is the case when you wrap legacy applications (such as COBOL) as objects, or when you use nonportable APIs such as TP monitors. The trick is that application architects and developers must think through this process carefully before committing objects to platforms.
Application partitioning is an architectural concept and leads into the discussion of the basic client/server architectures: two-tier, three-tier, and n-tier. (See Figure 1, page 26.) If you don't understand the fundamentals behind these base architectures, you should study them before you begin partitioning. In many respects, these architectures refer to available computers where the application processing takes place; but, more specifically, they refer to where the interface processing, business logic processing, and data processing occurs.
Typically, the architect is in charge of determining how to partition an application to best take advantage of the available resources and technology and meet the requirements of the application. Developers may do this for smaller applications, but application partitioning goes well beyond a single application's problem domain. In many instances, you're defining the infrastructure and architecture for future applications. There are external issues to consider, such as user load, network load, processing load, application type, costs, maintenance, and performance expectations. Let's look more closely at user and processing load, as well as application types and maintenance.
User load does not mean the number of users who will have access to the system; rather, it means the number of users who will access the system simultaneously and the behavior of the users. To a large degree, user load determines the architecture and application partitioning scheme because the number of partitions is really a function of the user and processing load requirements and the enabling technology in use. A common mistake is to try and scale through server sizing. Buying bigger processors to scale to larger user and processing loads might seem to make good sense, but it's the architecture and application partitioning scheme that determine true scalability. The fact is, we don't yet make servers that can handle 20,000 clients simultaneously; we must, therefore, resort to architectural tricks.
For instance, once you get up into the hundreds of users -- depending on their behavior -- you're reaching the saturation point for many database servers. This alone is sufficient reason to partition your application beyond the traditional two-tier client/server model. Database servers simply run out of resources (memory and CPU power) to support the one-connection-per-client model, and they die a tragic death. I've found the saturation number to be between 100 and 500 users, depending, of course, on the application they are running and the size of the servers. In this situation, you'll have to consider using a middle tier (or several middle tiers) to multiplex the database requests on behalf of the clients.
How do you determine the saturation point? Well, you'll have to read my column on client/server performance modeling in the July issue of DBMS (page 26). In considering the user and processing load, you must consider the types of applications you're partitioning. For instance, online analytical processing (OLAP), online transaction processing (OLTP), occasional use, and realtime applications all have different characteristics and requirements and, therefore, require different partitioning strategies. For example, OLTP applications, such as sales order-entry systems, use transactions to define units of work for the application. A single sale maps to a single transaction, and many transactions may occur in any given hour. Typically, OLTP applications require a three-tier or n-tier architecture with the transactions defined and processed at the middle tier for larger user loads. This architecture provides the greatest flexibility for the developer and enables the application to scale to higher transaction loads because the middle tier is also able to multiplex database requests. Several hundred users may appear as only a handful of connections to the database server.
Developers, even application architects, rarely consider the cost of maintenance when building client/server systems -- although these costs can be five times those of development. How you partition an application largely determines how maintainable it is. For example, placing the application logic at the client, as is the common practice in two-tier client/server, means that you'll have to update each client when the application logic changes. A better solution is to place the application logic at a middle or shared tier, which gives developers a single point of maintenance. Therefore, if tax tables change or business rules become obsolete, it's just a matter of updating code on a single server.
Another issue is failover. Partitioning redundant portions of applications across multiple servers enables the system to recover from network or server failures. Because more than one server is running a portion of the application, the server that is left running is able to take over for the server that is not. Application partitioning tools, such as Forté from Forté Software Inc., provide such native services. The trick is to eliminate all single-points-of-failure in mission-critical systems.
Static partitioning means that once you partition the application, it's going to be difficult to repartition if the need arises. TP monitors are classic examples of static application partitioning technology because they typically run code that's unable to run on either the client or the database server. Once the transaction services exist using the TP monitor API, they are typically bound to the TP monitor. I've found that, although more flexible than TP monitors, distributed objects (such as those defined by CORBA) have static features as well. Porting them from platform to platform may be easier, but a significant amount of work is involved, especially if you're using different object request broker vendors in heterogeneous environments.
When considering static application partitioning, make sure that you want that portion of the application to run on that particular tier or machine. If you have to make adjustments after deployment, count on significant cost in development time, testing, and debugging. Once again, performance modeling tools and techniques will ensure that your partitioning scheme won't fall on its face after deployment. Verification models and pilot testing will also reduce the risk of a mispartitioned application. Dynamic application partitioning means that the application architects and developers can repartition the application at any time for any reason. What's more, they don't have to endure a significant amount of work in doing so. This model gives you the ultimate flexibility because you don't have to commit to a partitioning scheme, as is the case with static partitioning. Proprietary application partitioning tools, such as Forté, Dynasty, from Dynasty Technologies Inc., and Cactus, from Information Builders Inc., provide dynamic application partitioning features. The tradeoff is the significant cost of such tools and their proprietary natures. These vendors are getting better, however, and now provide links to open technologies such as distributed objects and third-party middleware layers.
The next frontier in the dynamic application partitioning game is the use of components. Although it is not a new concept, the Web is driving technologies such as ActiveX and JavaBeans. (OpenDoc is not breathing, and I fear the worst.) Currently, these component technologies are making the largest impact with the interface. However, using the advent of Microsoft's Active Platform and products such as Microsoft's Transaction Server (MTS), ActiveX is quickly moving to the server; so it's going to be Microsoft's servers for now. ActiveX is still pretty homogeneous. Java is moving in similar server-side directions, and it is serving the heterogeneous market that Microsoft is presently leaving behind.
Creating your logical application partitioning model means that you're defining a conceptual architecture -- mapping certain application functions to logical computers. First, it's helpful to divide the application into logical objects. Typically, you'll find these objects in the object model you should have created for the application design process. These models not only identify the objects, but they also define behavior and interfaces. This is the importance of a sound object-oriented analysis and object-oriented design process, which I know some of you practice, although most do not.
Using the object model as a jumping-off point, you must first define what each object does at a high level. For example, some objects may interface with the user, while some are responsible for interfacing with the database. Still others provide simple application logic layers and may interface with both the user-interface and data-interface objects. Typically, I'll use a spreadsheet to lay out the objects, behaviors, and interfaces. For example:
Object: Sales_Order
Methods: Update(), Delete(), Append(), Abort()
Description: Enters a sale into the sales database.
Frequency: 34 bytes, 125 times an hour
DB Interfaces: Sales.DB, Order.DB, Customer.DB
Object Interfaces: Customer_Update, Order_Update
The next step is to group the objects around the presentation layer, the application layer, and the database access layer. This step requires looking at what each object does and placing it on the layer that makes the most sense. For instance, interface objects obviously should exist on the client nearest the user, where database-reporting objects should exist as close to the database as possible. General-purpose application objects, such as those objects that contain business logic, should exist within the application layer (which by the way should be the lion's share of your objects).
Having grouped the objects around the layers, it's a good idea to look at which objects are consistently communicating with which other objects and regroup the objects to minimize network traffic and increase reliability. Remember, this is just the logical partitioning scheme. You'll have an opportunity in the physical partitioning to change things. What's more, if the system is mission critical, it's a good idea to create redundant partitions and a mechanism to handle failover.
When considering the physical partitioning, the main point is to map the logical objects to physical machines and real enabling technology -- namely tools, distributed objects, database technology, TP monitors, and so on. This is the point where the application architect must commit to enabling technology (specific products to use) and hardware, and thus define the platform for partitioning the application. Also at this time, you determine the proper client/server model: two-tier, three-tier, or n-tier. If all was right with the world, three-tier and n-tier would always be the architectures of choice. However, two-tier is still much more cost effective, and if there is no real need to scale the application and you're dealing with only a handful of clients, two-tier is typically the best fit when considering cost (something we all have to do).
Physical application partitioning is no easy task because there are good and bad attributes for each class of enabling technology. Once again, I would recommend using a pilot testing process to ensure that you've made the correct design and performance decisions. This is also when you should refine the application design and partitioning model, considering the physical characteristics of the platform. For example, transaction services for TP monitors are built using very different techniques from those used for building application objects in a specialized client/server development tool.
Creating Web applications is simply taking the same concepts to a new set of platforms and enabling technologies. Currently, scaling has not been much of a concern because the Web architectures use stateless disconnected clients. You just purchase larger Web servers to handle the increased client load, and Web servers do little more than send files or HTML streams. What's more, because Web servers drive applications centrally, business logic -- by nature -- resides on a central server. Today, however, we are making inroads into Java and ActiveX, which bring dynamic application behavior to the desktop. As I mentioned in previous columns, these bring us back to traditional client/server, and we need to perform the same process to ensure that the application is split among available computers properly. We also must contend with distributed object technology, as well as middleware. We appear to be moving the Web back to traditional distributed computing. What goes around comes around, I always say.
The power of distributed computing is the ability to exploit the power of commodity computers by leveraging the distributed processing and client/server model. The trick is to balance the work evenly among all available resources in a way that's most reasonable for the business problem at hand. We may be feeling our way through the dark now, but application partitioning will eventually move from an art to a science.
Figure 1.

--Application partitioning is an architectural concept and leads into the discussion of the basic client/server architectures: two-tier, three-tier, and n-tier.
Figure 2.

--Static and dynamic application partitioning.
Figure 3.

--Defining your logical application-partitioning model and then mapping that model into a physical implementation.
David S. Linthicum is the author of David Linthicum's Guide to Client/Server and Intranet Development from John Wiley & Sons (1997). He's a widely published author, speaker, and professional consultant. You can email David at linthicum@worldnet.att.net.
What did you think of this article? Send a letter to the editor.