Building distributed applications with DCOM and MTS

Among the I/S shops preparing to move from DCOM to Microsoft Transaction Server (MTS) are many traditional two-tier development shops. Transaction processing is quite new to them, and the paradigm shift they must endure is quite substantial. This is perhaps one of the reasons why MTS technology, although strong, has not quite taken off like a rocket.

Add to this the fact that object technology is also new to many developers. For a number of years, the industry has been developing object technology. A typical definition of objects might assert that objects have a state. Applications also have state. The question being asked today, however, is how users should manage objects in a transactional environment such as MTS.

MTS is gaining momentum with the release of Version 2.0. Microsoft's new technology brings with it a new way of building distributed component architectures. A paradigm shift, that focuses on how an application implements state, is therefore required to move from Distributed Component Object Model (DCOM)-based applications to an MTS world. While MTS provides a basic support for state, it is insufficient for most appli-cations. There are two prevailing ways to implement state: client- and server-side state. Although it is possible to combine the two, this is not desirable in practice. MTS offers many advantages, but it does not replace traditional DCOM-based services. The challenge comes from integrating services.

There are a number of terms associated with MTS. MTS services are distributed as packages, which might have one or more components. Each component can implement one or more interfaces, and each interface has one or more methods. A business transaction is a process or workflow by which something is accomplished. Multiple components and several method invocations can be involved in one transaction.

The major evolution in MTS is a requirement to keep MTS components stateless. This allows greater performance through resource sharing, component management and a simplified programming model. Yet it also brings confusion to the distributed object world. Users must now answer such fundamental questions as "Where will the state of an application reside?" and "How will the state of a business transaction be captured?"

Most applications and business transactions have a state that can take various forms. An application might have information that is shared across many user interfaces or a transaction that spawns across invocations of different interfaces. In any case, data must be maintained somewhere and be available as needed.

MTS does not dictate where the state needs to be maintained. In fact, MTS evangelists encourage users to build stateless transactional services that have no state between invocations.

Given a business requirement and a suitable application design, an application architect's first choice may be to build an application using stateless service. If an application needs to maintain state, there are several choices available:

  • Use MTS mechanisms to maintain state;
  • Maintain a state on the client;
  • Maintain a state on the server;
  • Use a hybrid state integrating a state on the client and on the server;
  • Integrate MTS with DCOM, which maintains state on a server; and
  • Pass the state along at every invocation -- do not keep a state.

The last option is the simplest, because it requires no extra architecture consideration. All that is required is to pass along all of the data needed for each request. For a simple application this might be sufficient, but limitations may occur when it comes to a real-life application.

MTS support for a state

MTS has a simple mechanism that allows component services to maintain state. There are two basic ways to maintain state: Shared Property Manager (SPM) and ObjectContext.

SPM is a global name/value dictionary that allows sharing of global attributes of basic types: int, char, long, float, array. While SPM is a convenient way to share one or two attributes, it might not scale if the entire application state will be stored in the name/value dictionary. SPM is also not distributed across different servers. Therefore, if an application's components are distributed across multiple servers, SPM will not be able to share data across those servers. Additionally, SPM users must convert everything into generic name/value pairs.

ObjectContext is a state that is implicitly associated with a given MTS object. Context contains data about the object's
execution environment, such as the identity of the object's creator. An object's context is similar in concept to the process context that an OS maintains for an executing program. The MTS run-time environment manages the context for each object.

ObjectContext controls component state. In Version 2.0 of MTS, component state support is limited to several API calls that determine the life of the object. Each component can participate in determining the outcome of a transaction. SetComplete, SetAbort, EnableCommit and Disable-Commit can control the object life cycle based on the desired component behavior.

Life cycle goes on

Do users have control over the object life cycle? The answer depends on how the MTS component was marked when it was installed in the MTS server. Users can mark components in the following ways:

  • REQUIRES A TRANSACTION: This indicates that the component's objects must execute within the scope of a transaction. When a new object is created, its object context inherits the transaction from the context of the client. If the client does not have a transaction, MTS automatically creates a new transaction for the object.
  • REQUIRES A NEW TRANSACTION: This indicates that the component's objects must execute within their own transactions. When a new object is created, MTS automatically creates a new transaction for the object, regardless of whether its client has a transaction.
  • SUPPORTS TRANSACTIONS: This indicates that the component's objects can execute within the scope of their client's transactions. When a new object is created, its object context inherits the transaction from the context of the client. If the client does not have a transaction, the new context is also created without one.
  • DOES NOT SUPPORT TRANSACTIONS: This indicates that the component's objects should not run within the scope of transactions. When a new object is created, its object context is created without a transaction, regardless of whether or not the client has a transaction.

One drawback to ObjectContext control is that it does not allow application scaling past 50 to 100 concurrent clients. If there are 100 live objects in the server that maintains state, each object has its own database connection and will not participate in a connection pooling. Today, there is no way for an application to maintain the number of connections in the pool. The only way to maintain connection number is by using CPTimeout registry setting. However, this does not work very well as the connection will be destroyed and the expense of creating a new connection outweighs the benefits. Thus, maintaining state within services by not completing the transaction causes a resource requirement blowout. Thus, the first principle of building scalable MTS solutions is to call ObjectContext.SetComplete at the end of every interface invocation.

One increasingly accepted way of addressing common issues and architecting solutions is through the use of design patterns (See Design Patterns: Elements of Reusable Object-Oriented Software, by E. Gamma, et. al. Addison-Wesley Publishing Co., 1995). In particular, a Model/View/Controller (MVC) design pattern is often appropriate in addressing issues such as those raised by MTS implementation. For more on design patterns, please refer to "Design patterns take cue from architecture" (Application Development Trends, November 1997), as well as "AntiPatterns" (Application Development Trends, October 1998).

MVC is a universal pattern for separating user interface, data and control flow. It consists of three kinds of objects: the Model, which is the application object; the View or screen representation; and the Controller, which defines the way the user interface reacts to user input. To implement MVC one can use several design patterns: Composite, Factory Method, Observer and Strategy, for example.

The MVC can be used to:

  • Decouple views and models by establishing subscribe/notify protocols between them;
  • Provide a different presentation to a model through multiple views;
  • Design so that changes to one object can affect any number of others without requiring the changed object to know details of the others;
  • Create nested views; and
  • Change the way a view responds to user input without changing its visual presentation.

Client-side state management

One way to address application state persistence is to maintain application state on a client. There are several strategies to maintain state on a client:

  • Maintain state in user interface (UI) controls
  • Maintain state in business objects
  • Maintain state in ADO RecordSet

A standard design pattern, like MVC, can help implement client state management. In client-side architecture, Model implements application state. Model can be implemented by using business objects, database record sets or user interface controls.

Business Objects is an object-oriented, de facto standard that lets business and technical users capture and communicate application functionality. Business rules and data integrity can be enforced in business objects. Validation can be performed in a business object before it is stored in a database. Business Objects can also capture relationships between various objects.

A certain amount of overhead is involved in using Business Objects to implement Model. This implementation overhead comes from writing the extra code needed to unpack data from distributed calls, put it into Business Objects and populate Views with the data. All of the above steps might be necessary if an application wants to maintain all of its state in a Domain Business Objects Model.

Another way to implement Model is to use database record sets. Database record sets are represented with memory tables that contain a number of columns and rows populated as the result of a query. Microsoft provides Active Data Objects (ADO), which encapsulate open database connectivity (ODBC) database access into a number of high-level classes. RecordSet is one such class. To allow for the distributed transport of the RecordSet, Microsoft has implemented DCOM base marshaling. The ADO base RecordSet can therefore be disconnected from the database connection and transported from a server to a client.

One of the benefits of using ADO RecordSet is its distributed capabilities, which allow developers to skip the process of writing custom code for packing and unpacking the data. While this may save time during the rapid development phase, it may complicate data maintenance in the long term and decrease the reusability of the developed component. Thus, RecordSets can be pushed all the way to the client from the server without a change.

RecordSets force Views to know the database structure and query result structure in order to read or write records sets. However, it can become hard to maintain a changing state in record sets. Long term, when application complexity increases and upgrades need to be released, using RecordSets might become an issue.

Once record sets are delivered to the client side of an application, they can be grouped into Business Objects. One way to address some of the challenges of using record sets is to partially convert record sets in the client application into Business Objects.

View represents various forms of presentation services, which can be implemented via user interface screens and controls. It also allows an application to present a different look at the Model. View interacts with a controller for data and business functionality requests. In turn, the controller notifies View about new data in the Model as it becomes available. View then accesses the Model in order to populate the user interface with data. View can be implemented in any language/tool: Visual Basic, Visual C++, Visual J++ and so on.

Controller controls an application's business functionality and transactional flow. It functions like a traffic light, and is responsible for the smooth operation of traffic between clients and servers. Controller communicates with distributed components to obtain data and populate the Model. The Controller can also be called a business transaction, a workflow or a process -- the meaning is the same. Application logic is executed on the server and the result appears on the client machine.

To put our MVC design pattern into perspective, we will use the following application scenario: user selects employee search screen; user fills in employee last name; user presses Find button; and a list of employees with the same last name is populated on the screen.

When the user selects the employee search screen, a new View -- Employee Search -- is created. Once the user has clicked on the Find button, the Employee Search View invokes a method called findEmployee on the Employee Controller object. As a result of the execution of a method, the Employee Controller creates an Employee List Business Object that maintains a list of selected employees. Controller then notifies View that the data is available. View can now iterate through the Employee List Business Object in order to populate the Employee List data.

Client-side state is one of the design patterns that can be used to implement MTS base applications. The following is a short list of considerations that are helpful when choosing this type of architecture:

  • Client computer allows for a lot of memory and a fat client.
  • There is no state and data sharing between different clients.
  • Transactions spawn across three or more service interface calls.
  • It is costly to store and restore state in the database on the server.
  • When legacy components exist and can be integrated on a client only.

Server-side state management

State can also be maintained on a server. As discussed earlier, Shared Property Manager and ObjectContext are two ways in which state can be held on a server. But neither one of these options provides a scalable solution. There are two alternatives, however: implementing server-side state using a database, and implementing server-side state using a stateful server.

Many Microsoft evangelists suggest keeping an application state in a database. However, we will use a familiar MVC design pattern to implement state using a database. First, while server-side state management employs the same MVC design pattern discussed previously, there are some differences. In this scenario, Controller and Business Service become one, and Controller and Model have been moved to the server side. View responsibilities have stayed the same, although View now accesses a distributed business service directly, rather than going through Controller before populating View from the Model. It is important to note that even though the same design pattern is used, the Business Services interfaces could be different.

Because there is no business logic on a client side, this architecture allows for a thin client, suitable for Network Computers or browser-based interfaces. Controller and Business Services can also be integrated. Controller is implemented as a component, which has a set of distributed interfaces to access its functionality. View can directly access controller interfaces and request business services. Controller then uses Model to complete business functions.

Server-side architecture lets Model implement an application state. There are two ways to implement Model: Using business objects or database record sets.

A Model that is represented using Business Objects must restore the state of those business objects from the database before it can be used. For example, a business transaction might spawn across several service interface calls and require the availability of a partial or complete Business Object model. This means that Business Object state must be restored every time. To restore a state in the Model, one or more database invocations are required.

There is also a need for balance between application design and the need for frequent trips to the database. As a rule, if an application restores a state more then three times within one business transaction, users should consider using client-side state or redesign the business transactions.

To put the MVC design pattern for server-side state management into perspective, we use the following application scenario: user selects employee search screen; user fills in employee last name; user presses Find button; and a list of employees with the same last name is populated on the screen.

In this instance, the user selects the employee search screen to create a new View, "Employee Search." Once the user clicks on the Find button, the "Employee Search" View invokes a method called findEmployee on the Employee Controller object. As a result of the execution of a method, Employee Controller creates an Employee List Business Objects that maintains a list of selected employees. Note the difference from client-side state management: Model, or the Employee List Business Objects, is created on a server. Controller transports all found employees to View, which then uses the data to populate the Employee List control. As with the previous example, we did not have to create an Employee List Business Object. We could have used an Employee List ADO RecordSet transferred from a server.

Server-side state is one design pattern that can be used to implement MTS base applications. The following is a short list of considerations that are helpful when choosing this type of architecture:

  • Client-side application will be deployed on a Network Computer or browser, or there is a low resource requirement;
  • Transaction does not spawn more than three service interface calls;
  • Transactions are very complex;
  • Implementation of transaction changes often;
  • More than one client is involved in a transaction; and
  • Data required for a transaction does not need to be shipped to the client.

Migrating hybrids

Here, we have only considered exclusive architectures, such as client- or server-side state management. While there is nothing to prevent users from combining both architectures, application complexity will increase. This makes the application harder to maintain, and makes it harder to decide whether the application's state should be kept on a server or a client.

It is important to note that modern distributed applications require a combination of stateful and stateless services. A good example of a stateful service would be one that provides static and read-only lookup data. Static data does not change often and is required for a drop-down or selection list.

In defining MTS, Microsoft went to great lengths to simplify some aspects of object design. Yet questions remain, such as: How do you apply object-oriented analysis and design to a transactional environment? This is not an easy question to answer. To build scalable solutions for MTS, users need to build stateless objects on the server. The use of design patterns can also overcome some of the obstacles encountered when navigating such a paradigm shift.