Building distributed applications with DCOM and MTS
- By Vlad Kroutik
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
- 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
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
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.
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.