Return to Kinexis Home Page
 

 
Discovering a Three-Tier Architecture for Distributed Object  Systems 

by John Tibbetts and Barbara Bernstein  

Distributed Computing Magazine, March 1998 



The best object systems will have three tiers, just not the three tiers we thought. 


   
By the time object technology grew robust enough to take on the job of building distributed systems, three-tier had become established as the architectural model of choice.  The consensus held that nothing less could handle the demands of scalable, mission-critical, heavy-duty applications. In order to compete in the big leagues, object systems adopted three-tier terminology almost automatically, and we have seldom looked back. 
     (We pause here to clarify what we mean by “three-tier,” so fuzzy has the term become.  We are talking here about an architectural definition of three-tier, the division of an application into pieces that perform, by one name or another: 1) presentation to the user, 2) execution of business logic, and 3) access to the database. For purposes of this article, we ignore the opportunistic, vendor-driven uses of “three-tier” to indicate how many machines the application spreads itself over, or on which platform any particular piece executes.) 
     We believe that implementing “three-tier architecture” using object technology should not mean simply recasting, in objects, the same divisions and relationships that we saw in non-object systems (which we call here “procedural” or “classical” systems). The whole notion of  tiers takes on quite a different flavor in the object world. For one thing, objects let us decide with more precision which part of the system should assume responsibility for which activity.  Different tiers hold different kinds of objects, so tier assignments can be made, in general, on the basis of pedigree rather than by making often-arbitrary judgements about whether something is  business logic or user-interface logic. 
     Even more important, in an object system the nature of the tiers themselves shifts.  Behaviors get refactored and end up distributed among architectural categories significantly different from those in the procedural world. The differences are so profound, we hope to show, that one whole tier disappears, and a new one—populated by an entirely new kind of object—emerges. 
 
Two-Tier OO 

If we try to compare the old User Interface/Business Logic/Data Access triad with what happens in object applications, we see both correspondences and differences. The front tier stays roughly the same.  What is usually called the Presentation tier in non-OO systems corresponds closely to what we call “consumer-interface objects.” Typically these are “viewers” or “view objects” that render the non-visual objects found in other tiers of the architecture. We call them “consumer-“ rather than “user-“ interface objects because, increasingly, they need to support not only humans using GUIs or browsers but also non-human programmable agents at work on various system assets. 
     If the front end is similar, the back end is quite different. OO tends to take business logic that was previously located in Tier 2, and persistence logic formerly found in tier 3, and combine them into back-end entities called “domain objects,” which model the fundamental assets of the business (Employee, Part, Subscriber, Account, etc.) After all, object thinking encourages us to factor entities according to the application domain they belong to rather than the specific kind of behavior they incarnate. So, for example, an Employee object may encapsulate behavior for validations and transformations (“business logic”) as well as for accessing and persistence (“data access”). 

     Some object architectures, it is true, explicitly picture a data access tier behind the domain objects tier. The objects here, according to this theory, depict the persistence machinery for mapping domain objects back and forth to the datastores at the far back end. While we acknowledge that there is no right way or wrong way to create architectural diagrams (they are, after all, merely tools to help us organize our thinking), we believe that this extra tier is neither useful nor logical. First, such persistence machinery seems to us part of the architectural infrastructure, just as transactions, security, and naming are. It doesn’t materially add to the way we think about the structure of the application.  Second, some environments (such as in an object database) actually include persistence in their object model; in these cases, a separate layer becomes superfluous. Finally, adding “data access” to an OO application architecture is something of an architectural mixed metaphor. After all, “data” per se is not supposed to exist when we have objects everywhere. 
     We would maintain, then, that the essential elements of most of today’s distributed object systems today are viewer objects on the front and domain objects on the back, wired together with a bit of hand-coding in the middle. These are essentially two-tier systems, though few would care to admit it. 
     This conviction that we are implementing three-tier systems when we actually aren’t has kept us from coming up with a fresh three-tier model that is truly suited for how objects behave and what we can do with them. 
     What makes us think that there is an as-yet undiscovered application structure here?  Because distributed object systems are harder to build than we expected them to be.  That “little bit of hand coding” referred to above turns out to be large stretches of application-specific code written from scratch without even standardized procedures to follow. Wiring together the two tiers of these applications has in practice consumed probably as many developer hours as the creation of domain and view objects put together. As any builder of models knows, when the layer of glue becomes as thick as the things it’s holding together, you have to wonder if there isn’t a piece missing in the middle. 
     What is missing in this case is a bona fide middle tier, unique to object systems and quite unlike the old, vague “business logic.” It would be the home of a new kind of object, one that stands between view objects and domain objects and performs a newly-defined job of handling mediation and communication between them. If we can accurately understand the function of this new middle tier, find the right name for the objects that live there, and come up with state and behavior to get the job done, we can uncover opportunities for reuse in a large application stretch that currently has none. 
 
Three-Tier OO 

     How can we generalize about what goes on at the middle tier of any application—NewHire, TransferInventory, ScheduleShipment, DispenseCash or what have you?  We suggest the following: This is the place where the intentions of the client side are formed into requests that will subsequently be handed to the domain side. 
     These requests have a function and a personality that is not like anything we have seen before. 
     We call them Proposal objects, for their job is to “propose” changes to enterprise assets. When they join the other two kinds of objects in a new OO triad, system responsibilities suddenly fall into a three-tier division that is so logical it seems to have been waiting for us all along. We have domain objects, which represent enterprise assets; Proposal objects, which represent proposed changes to domain objects; and viewers, which offer a way to look at and interact with either. 
 
The Role of Proposals 

     A Proposal object abstracts the common behavior involved in putting together requests for updates to back-end domain objects. Its infrastructure should handle organization and navigational behavior, and handles the bulk of the mapping between the input gathered via consumer interface objects and the information required by domain objects.  It could also include advanced transaction-forming behaviors such as document navigation, error-and annotation-handling, versioning, archiving, and stale-data detection. 
     A good way to understand the power of Proposal objects is to contrast them with domain objects.  Domain objects are definitive. They represent committed enterprise assets such as Employee, Part, Subscriber, Account.  They have to be correct, complete, authoritative, in synch with one another, and durable (that is, so constructed that they can survive machine failures). 
     Proposals, by contrast, model unfolding, often multi-step and multi-party processes.  Examples of Proposal objects are NewHire, CreateInvoice, MeritIncrease, ResubscribeUser. Since they are only  tentative suggestions to modify enterprise assets (these suggestions will have to pass all enterprise screens before they are committed), Proposals can be fallible. They are meant to be worked on, refined, changed, passed around, reviewed, and shaped over time. They bring “mulling-over” and “trying-out” space into a system. 
     Thus, Proposals can be imperfect and incomplete.  They can tolerate errors, noting and marking them as a kind of “to-do” list.  They can be disconnected from the authoritative store of domain objects.  They are kept in their many successive versions— they are undo-able and redo-able—with the version list being available for detailed auditing purposes. They can carry annotations around with them, as vehicles for discussing—we might say, negotiating—eventual transactional requests. In short, they are very much like proposals in the human world—concrete “things” that embody the process of collaboratively formulating something that may or may not eventually become binding. 

     In order to do the job we have laid out for them, Proposal objects have a number of responsibilities, implemented as state and behavior. Most important, Proposals must be introspective, and know a great deal about their own metadata. They have to act as containers for the data related to a specific Proposal. They must keep versions of themselves. They should retain and track errors noted by the infrastructure, and also manage user-written annotations. They need a mechanism for protecting against stale data. In addition, Proposals must know how to interact with the back-end server, whatever it may be, and, on the other end, to interact with a variety of consumer platforms. 

     We have said that Proposals occupy the “middle” of our re-envisioned OO three-tier architecture. This means only that they live somewhere “behind” the consumer objects and “in front” of the domain objects, but we’re left with a wide range of possibilities.  With appropriate middleware connecting them to their architectural neighbors, they can live anywhere. In fat-client environments they might live at the workstation. In thinner-client environments they might live at the server. They could occupy a Proposal server of their own. 
     In fact, they can move as needed. This is a new development in distributed object design, which has in the past focused on relatively immobile objects that are essentially locked to a particular platform (domain objects hang out close to a database somewhere, viewer objects cluster near the glass). But Proposal objects have no geographical allegiances. A Proposal object can be initiated on the server, routed to client for further work, routed sideways to collaborators for further input and refinement, worked on by non-human agents, and then routed back to domain object to commit proposed changes. 
     This is one reason why Java, with its ability to support mobile objects, is the ideal development platform. In fact, the idea for Proposals has taken final shape only since the advent of Java. 
 

When To Use Two-Tier vs. Three-Tier

     Clearly the Proposal-object middle tier adds a new level of power to an application architecture, and also a new level of complexity.  When is the added investment justified? 
     Two-tier architecture—the viewer/domain duo described early in this paper—will  serve well when: 
  • The application is a simple viewer or editor of the domain objects.  A kiosk in a video store may simply need to get at a collection of movies available and filter them based on user preference.
  • The transaction being performed is innately “conversational.”  This is where the user is connected to the domain objects from the moment that the transaction starts to the moment that it completes:  an Automated Teller Machine begins and finishes its job while the user stands in front of it.
     Three-tier architecture including Proposals adds a great deal of infrastructure for handling more demanding business processes. It will be invaluable when: 
  • The transaction is collaborative. In reengineered environments a change to domain objects is also the basis for horizontal communication within the organization.  For example, a manager might initiate a MeritIncrease Proposal complete with suggested new salary level, annotate it to give his reasons for that raise, and forward it to a superior for discussion.
  • The transaction is completed in phases. E-commerce is a good example here.  A subscription-renewal proposal (for example, renewing a software support agreement) could be partially filled out by the support company and emailed to the customer. The customer can open the Proposal, choose the renewal term, and email it back to complete the transaction.
  • The transaction needs to be accessed by many kinds of consumers.  A customer service request my be created by caller-ID equipment and immediately transferred to a service rep’s terminal screen for follow-up.
  • The transaction is interruptible. A lengthy multi-page Proposal (for example, a NewHire transaction) can be pended, saved, and continued on the following day.
  • Detailed audit trails are needed. Most current systems keep audit trails once transactions complete. But Proposals can log much more detailed information, such as which of the Proposal’s users changed which field when.  This information remains available even if  subsequent users further changed the field before the Proposal was committed.
  • The application supports componentized construction. Since the Proposal carries lots of metadata about itself, its fields, its structure, and so forth, it works very well with “Proposal-aware” components like Java Beans or ActiveX controls.
Adding Workflow 

     Looking at the list of applications that call for three-tier OO, it’s easy to see why Proposal thinking leads directly to workflow (or vice versa). The synergy between the two is so irresistible that many people confuse them on first encounter. Actually, the difference is quite simple. Workflow is the train that routes content through your organization; Proposals are an architected, reusable way of defining that transactional content—the containers that the train carries, if you will. 
     We like to depict workflow as a separate layer that, in an architectural chart, would appear over  the consumer-interface/Proposal/domain-object tiers.  We call the objects in this box “FlowControllers” to give them an agent flavor. 

     In general, the FlowControllers are used to dispatch and route Proposals. When a Proposal is finally committed, the domain objects get updated and the flow is completed.  There are interesting possibilities for cascades here: the domain object, when changed, can actually generate new Proposals and hand them back to the workflow system. For example, a NewHire Proposal has been filled out and committed. As soon as the resultant new Employee domain objects is created, it might fire a business rule that causes several new Proposals (such as BenefitsEnrollment, ParkingSlotRequest, and PhotoBadgeAppointmentRequest) to be generated and routed to the appropriate locations. 
 
Conclusion 

Object technology’s insistence on finding the right name for behavior is vindicated once again.  Adding objects called “Proposals” to distributed object systems solves a number of problems and makes the entire 3-tier structure snap into new focus. Just as they do in the human world of buying, selling, delivering work for pay, arranging complex deals, and even deciding to get married, Proposals provide a needed intermediate step between a rigorously controlled and highly valued assets on one end, and free-spirited consumers with ideas for changing those assets on the other. 
     We are at the right moment in the development of distributed object computing to look carefully at application structures. The infrastructure issues have been by and large settled, and now we are getting down to real work. As we might have expected, the most useful model for robust transactional systems turns out to be a three-tier one after all.  They just aren’t the three tiers we thought they were. 

#    #    #
 
 
Return to Kinexis Home Page