April 01, 2003
Errant Architectures
When we let objects wander, we all pay the performance price. Here's how to avoid distributed dystopia's overhead of remote procedure calls and ignore middleware's siren song.
Martin Fowler
Objects have been around for a while, and sometimes it seems that ever since they were created, folks have wanted to distribute them. However, distribution of objects, or indeed of anything else, has a lot more pitfalls than many people realize, especially when they're under the influence of vendors' cozy brochures. This article is about some of these hard lessons-lessons I've seen many of my clients learn the hard way.
Objects have been around for a while, and sometimes it seems that ever since
they were created, folks have wanted to distribute them. However, distribution
of objects, or indeed of anything else, has a lot more pitfalls than many people
realize, especially when theyre under the influence of vendors cozy
brochures. This article is about some of these hard lessonslessons Ive
seen many of my clients learn the hard way.

[click for larger image]
Architects Dream, Developers Nightmare
Distributing an application by putting different components on different
nodes sounds like a good idea, but the performance cost is steep.
|
A Mysterious Allure
Theres a recurring presentation I used to see two or three times a year
during design reviews. Proudly, the system architect of a new OO system lays
out his plan for a new distributed object systemlets pretend its
some kind of ordering system. He shows me a design that looks rather like Architects
Dream, Developers Nightmare with separate remote objects for customers,
orders, products and deliveries. Each one is a separate component that can be
placed in a separate processing node.
I ask, Why do you do this?
Performance, of course, the architect replies, looking at me a
little oddly. We can run each component on a separate box. If one component
gets too busy, we add extra boxes for it so we can load-balance our application.
The look is now curious, as if he wonders if I really know anything about real
distributed object stuff at all.
Meanwhile, Im faced with an interesting dilemma. Do I just say out and
out that this design sucks like an inverted hurricane and get shown the door
immediately? Or do I slowly try to show my client the light? The latter is more
remunerative, but much tougher, since the client is usually very pleased with
his architecture, and it takes a lot to give up on a fond dream.
So, assuming you havent shown my article the door, I suppose youll
want to know why this distributed architecture sucks. After all, many tool vendors
will tell you that the whole point of distributed objects is that you can take
a bunch of objects and position them as you like on processing nodes. Also,
their powerful middleware provides transparency. Transparency allows objects
to call each other within or between a process without having to know if the
callee is in the same process, in another process or on another machine.
Transparency is valuable, but while many things can be made transparent in
distributed objects, performance isnt usually one of them. Although our
prototypical architect was distributing objects the way he was for performance
reasons, in fact, his design will usually cripple performance, make the system
much harder to build and deployor both.
Remote and Local Interfaces
The primary reason that the distribution by class model doesnt work has
to do with a fundamental fact of computers. A procedure call within a process
is extremely fast. A procedure call between two separate processes is orders
of magnitude slower. Make that a process running on another machine, and you
can add another order of magnitude or two, depending on the network topography
involved.As a result, the interface for an object to be used remotely must be
different from that for an object used locally within the same process.
A local interface is best as a fine-grained interface. Thus, if I have an address
class, a good interface will have separate methods for getting the city, getting
the state, setting the city, setting the state and so forth. A fine-grained
interface is good because it follows the general OO principle of lots of little
pieces that can be combined and overridden in various ways to extend the design
into the future.
A fine-grained interface doesnt work well when its remote. When
method calls are slow, you want to obtain or update the city, state and zip
in one call rather than three. The resulting interface is coarse-grained, designed
not for flexibility and extendibility but for minimizing calls. Here youll
see an interface along the lines of get-address details and update-address details.
Its much more awkward to program to, but for performance, you need to
have it.
Of course, what vendors will tell you is that theres no overhead to using
their middleware for remote and local calls. If its a local call, its
done with the speed of a local call. If its a remote call, its done
more slowly. Thus, you pay the price of a remote call only when you need one.
This much is, to some extent, true, but it doesnt avoid the essential
point that any object that may be used remotely should have a coarse-grained
interface, while every object that isnt used remotely should have a fine-grained
interface. Whenever two objects communicate, you have to choose which to use.
If the object could ever be in separate processes, you have to use the coarse-grained
interface and pay the cost of the harder programming model. Obviously, it only
makes sense to pay that cost when you need to, and so you need to minimize the
number of interprocess collaborations.
For these reasons, you cant just take a group of classes that you design
in the world of a single process, throw CORBA or some such at them and come
up with a distributed model. Distribution design is more than that. If you base
your distribution strategy on classes, youll end up with a system that
does a lot of remote calls and thus needs awkward, coarse-grained interfaces.
In the end, even with coarse-grained interfaces on every remotable class, youll
still end up with too many remote calls and a system thats awkward to
modify as a bonus.

[click for larger image]
A Better Way
Clustering involves putting several copies of the same application on different
nodes. If you must distribute, this approach eliminates the latency problems. |
Laying Down the Law
Hence, we get to my First Law of Distributed Object Design: Dont
distribute your objects!
How, then, do you effectively use multiple processors? In most cases, the way
to go is clustering (see A Better Way). Put all the classes into
a single process and then run multiple copies of that process on the various
nodes. That way, each process uses local calls to get the job done and thus
does things faster. You can also use fine-grained interfaces for all the classes
within the process and thus get better maintainability with a simpler programming
model.
Where You Have to Distribute
So you want to minimize distribution boundaries and utilize your nodes through
clustering as much as possible. The rub is that there are limits to that approachthat
is, places where you need to separate the processes. If youre sensible,
youll fight like a cornered rat to eliminate as many of them as you can,
but you wont eradicate them all.
-
One obvious separation is between the traditional clients and servers of
business software. PCs on users desktops are different nodes to shared
repositories of data. Since they are different machines, you need separate
processes that communicate. The client/server divide is a typical interprocess
divide.
-
A second divide often occurs between server-based application software
(the application server) and the database. Of course, you can run all your
application software in the database process itself, using such things as
stored procedures. But often thats not practical, so you must have separate
processes. They may run on the same machine, but once you have separate processes,
you immediately have to pay most of the costs in remote calls. Fortunately,
SQL is designed as a remote interface, so you can usually arrange things to
minimize that cost.
-
Another separation in process may occur in a Web system between the Web
server and the application server. All things being equal, its best
to run the Web and application servers in a single processbut all things
arent always equal.
-
You may have to separate processes because of vendor differences. If youre
using a software package, it will often run in its own process, so again,
youre distributing. At least a good package will have a coarse-grained
interface.
-
Finally, there may be some genuine reason that you have to split your application
server software. You should sell any grandparent that you can get your hands
on to avoid this, but cases do come up. Then you just have to hold your nose
and divide your software into remote, coarse-grained components.
The overriding theme, in OO expert Colleen Roes memorable phrase, is
to be parsimonious with object distribution. Sell your favorite
grandma first if you possibly can.
|
Working with the Distribution Boundary
Remote Façade and
Data Transfer Object are concepts to make remote architectures work.
As you design your system, you need to limit your distribution boundaries
as much as possible, but where you have them, you need to take them into
account. Every remote call travels on the cyber equivalent of a horse
and carriage. All sorts of places in the system will change shape to minimize
remote calls. Thats pretty much the expected price. However, you
can still design within a single process using fine-grained objects. The
key is to use them internally and place coarse-grained objects at the
distribution boundaries, whose sole role is to provide a remote interface
to the fine-grained objects. The coarse-grained objects dont really
do anything, but they act as a façade for the fine-grained objects.
This façade is there only for distribution purposeshence
the name Remote Façade.
Using a Remote Façade helps minimize the difficulties that
the coarse-grained interface introduces. This way, only the objects that
really need a remote service get the coarse-grained method, and its
obvious to the developers that they are paying that cost. Transparency
may have its virtues, but you dont want to be transparent about
a potential remote call.
By keeping the coarse-grained interfaces as mere façades, however,
you allow people to use the fine-grained objects whenever they know theyre
running in the same process. This makes the whole distribution policy
much more explicit. Hand in hand with Remote Façade is Data
Transfer Object. Not only do you need coarse-grained methods, you
also need to transfer coarse-grained objects. When you ask for an address,
you need to send that information in one block. You usually cant
send the domain object itself, because its tied in a web of fine-grained
local inter-object references. So you take all the data that the client
needs and bundle it in a particular object for the transferhence
the term. (Many people in the enterprise Java community use the term Value
Object for Data Transfer Object, but this causes a clash with
other meanings of the term Value Object.) Data Transfer Objects
appear on both sides of the wire, so its important that it not reference
anything that isnt shared over the wire. This boils down to the
fact that Data Transfer Objects usually reference only other Data
Transfer Objects and fundamental objects such as strings.
M. Fowler
|
|
Interfaces for Distribution
Use Web services only
when a more direct approach isnt possible.
Traditionally, the interfaces for distributed components have been based
on remote procedure calls, either with global procedures or as methods
on objects. In the last couple of years, however, weve begun to
see interfaces based on XML over HTTP. SOAP is probably going to be the
most common form of this interface, but many people have experimented
with it for some years.
XML-based HTTP communication is handy for several reasons. It easily
allows a lot of data to be sent, in structured form, in a single round-trip.
Since remote calls need to be minimized, thats a good thing. The
fact that XML is a common format with parsers available in many platforms
allows systems built on very different platforms to communicate, as does
the fact that HTTP is pretty universal these days. The fact that XML is
textual makes it easy to see whats going across the wire. HTTP is
also easy to get through firewalls when security and political reasons
often make it difficult to open up other ports.
Even so, an object-oriented interface of classes and methods has value,
too. Moving all the transferred data into XML structures and strings can
add a considerable burden to the remote call. Certainly, applications
have seen a significant performance improvement by replacing an XML-based
interface with a remote call. If both sides of the wire use the same binary
mechanism, an XML interface doesnt buy you much other than a jazzier
set of acronyms. If you have two systems built with the same platform,
youre better off using the remote call mechanism built into that
platform. Web services become handy when you want different platforms
to talk to each other. My attitude is to use XML Web services only when
a more direct approach isnt possible.
Of course, you can have the best of both worlds by layering an HTTP interface
over an object-oriented interface. All calls to the Web server are translated
by it into calls on an underlying object-oriented interface. To an extent,
this gives you the best of both worlds, but it does add complexity, since
youll need both the Web server and the machinery for a remote OO
interface. Therefore, you should do this only if you need an HTTP as well
as a remote OO API, or if the facilities of the remote OO API for security
and transaction handling make it easier to deal with these issues than
using non-remote objects.
In this discussion, Ive assumed a synchronous, RPC-based interface.
However, although thats what Ive described, I actually dont
think its always the best way of handling a distributed system.
Increasingly, my preference is for a message-based approach thats
inherently asynchronous. In particular, I think theyre the best
use of Web services, even though most of the examples published so far
are synchronous.
For patterns on asynchronous messaging take a look at www.enterpriseIntegrationPatterns.com.
M. Fowler
|
Martin Fowler is Chief Scientist at ThoughtWorks and a frequent speaker at
Software Development conferences. This article is adapted from Patterns
of Enterprise Application Architecture, Chapter 7 (Addison-Wesley, 2003).
Reprinted with permission.