Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Scalable Architectural Guidelines: Designing for Flexibility : Page 4

Both stateless and stateful components have a legitimate role in any scalable system architecture. This applies equally to both Client Server and n-Tier architectures. This article covers basics and not-so-basics concepts related to scaling up and scaling out.


advertisement

Don't Get Chatty with the Foreigners
Let’s discuss chatty’ vs. chunky’ relationships. The chatty client communicates with its server in an object oriented manner. It instantiates the object, and then proceeds to have a chatty back and forth dialog with the server by calling various methods, and by setting and/or retrieving various properties. After finishing its chatty conversation, the client finally terminates the object.

The chunky style of communication is much more abrupt. The client instantiates the object. It then calls a minimal number of methods, ideally only a single method, which returns a whole slew of relevant information in a single large chunk. The client then immediately releases the object into oblivion.

In a nutshell, while chatty calls are fine in an intra-tier situation, chunky calls are more appropriate for an inter-tier scenario.

In order to make OO developers feel a bit more comfortable, I’d like to point out a couple of dominant areas of inter-tier communication which are using the new chunky style of communication, and have been for quite some time already.

SQL & ADO: Way back when, I used to program using COBOL and a non-relational IMAGE database (what a database!) on the HP-3000. Both the client programs as well as the database were local to each other on the HP-3000. The standard processing paradigm in those days was record oriented. We’d establish a path (keyed or sequential) into a dataset and we’d then begin reading, processing each record in turn.

In contrast, SQL, and subsequently ADO, arrived on the scene designed to support clients which are remote to the database which they access. SQL is by nature set oriented. A given SQL statement need not operate on a single row, rather SQL is specifically designed to be chunky by supporting set oriented operations. ADO as well is quite chunky when you think about it. The disconnected recordset feature is specifically designed to marshal large chunks of data back and forth between server and client.

HTTP: Communications between the browser and the web server are incredibly chunky. Consider a standard form POST. The entire form contents are sent up to the server along with the query string parameters and cookie data. All of this data is processed during this single transaction and subsequently an entire page, along with any cookie data, is returned back to the browser and the connection is closed. It just doesn’t get any chunkier than that!

As n-tier developers, we need to take our cues from these two mechanisms which are actually two major mainstays of our own development environments. When it comes to inter-tier communication, chatty is out, chunky is in. We can also take a cue from the fact that, despite the chunkiness of the underlying data stream, both of these facilities provide objects at the receiving end of the data, in order to maintain the data and provide access to it. For example, ADO provides the Recordset and other objects, while IIS provides the Request and Response objects which encapsulate and provide access to the incoming and outgoing HTTP streams.

Here are two basic design patterns which I use to develop chunky server interfaces.

Complex Data Classes:
Let’s say we have a server class which provides a single, albeit complex, set of data. This is the type of scenario which, following the OO design patterns, would employ a stateful object with methods and properties, to allow a chatty client to retrieve the information piecemeal and process it accordingly. Thus, it is the complex interface presented by the class which provides structured and convenient access to the data. For a chunky conversation we don’t need a complex interface since we’ll only be making a single method call. What we need is for this single method to return a complex data structure containing all of the relevant data.

There are several approaches with which we can accomplish this. Of course, each approach must return a data type which will physically marshal between tiers. Otherwise, accesses to a non-marshaling object will actually be crossing a physical tier for each access. If the data already exists in recordset format, I’ll generally return a disconnected recordset which marshals nicely between physical tier boundaries. Otherwise, my favorite approach is to use XML, since XML is specifically designed to maintain any complex data structure in a simple string format. To accomplish this, the server class uses a DOM (either its own or via a helper class wrapper) to store the data, instead of using private class variables. The entire DOM can then be serialized via the .xml property and returned to the client as the functional return of the chunky method.

The client is now free to manipulate this data as it wishes. For simpler structures it can simply load a DOM and access the various nodes. For more complex data structures, I usually provide some sort of helper class which can be used by both the server and the client to interface with the DOM. The server uses the helper class to instantiate the DOM by having a chatty conversation with, and setting properties of the helper class. This is OK since this is an intra-tier relationship. The helper class provides a read/write .xml property for serializing the DOM to a string, or for loading the internal DOM from a string. After setting all the information via the helper class interface, the server class retrieves the serialized XML string and returns it to the client. The client, for its part, instantiates its own helper class and loads the serialized XML into the helper class’s internal DOM. It then has its own chatty conversation with the helper class, retrieving various bits of information from various methods and properties of the helper class’s complex interface. Again, this chatty conversation is fine since it is strictly intra-tier.

A few quick points:

This helper class paradigm is an almost perfect match to an ADO disconnected recordset which is serialized to marshal across physical tier boundaries, yet presents itself via a class hierarchy when it arrives at the other end. Of course, ADO provides its own custom marshaler which takes care of instantiating and loading the ADO classes automatically. Without writing a custom marshaler for our helper class, we have to take the poor man’s approach and explicitly load the class when the serialized data arrives at either end. The end result is the same though. A serialized stream of information which marshals well across physical tier boundaries along with the convenience of access through a defined interface at both ends.

In addition to the helper class’s defined interface, I always provide access to the internal DOM, via a .DOM property. This allows new information to be added to the DOM quickly, without needing to modify the interface. The drawback to this is that this new data is not defined via the interface and requires direct DOM access as well as explicit communication between the client and server developers as to how this item will be stored and accessed. But hey, sometimes time-to-market considerations just can’t be denied. Of course, these little idiosyncrasies are always corrected by evolving the interface to account for the new data item during the next scheduled development cycle.

Naturally, the helper classes must be installed on both the client and server tiers. There’s nothing out of the ordinary about this, nor does this indicate any deficiency to the application architecture. As part of the supporting application framework, helper and general utility classes are routinely installed across multiple tiers. Obviously, ADO itself must be installed on all tiers on which it is used. So must the VB runtime DLL’s, or the .Net framework, or MTS, or COM+ for that matter.

Repetitive Access Classes:
Another type of class is the repetitive access class, which provides a method which must be called multiple times in a single transaction. Depending on the complexity of the data which it returns I’ll either use XML as described above, or a simple return array. The question is really, how to engineer a single method call through which the client can provide sufficient data for multiple logical operations, so that the server method need only be called once for each transaction.

My standard approach is to pass in parameters as arrays, rather than as simple data types. That way, the server has enough information on hand to iteratively process all information in a single method call. A variation on this approach, where the repetitive access is the exception rather than the rule, is to pass in the parameters as variants and have the server interrogate the parameters to see whether they are arrays or simple data types and then process them accordingly. (This provides convenience for clients which might wish to call these methods using simple data types rather than arrays.)

In either case, the server method is designed to operate iteratively on one or more sets of parameters, and to return the entire chunky result set. As long as the client is in possession of sufficient data up-front, the ability to pass in multiple sets of parameters means that the client can make multiple logical’ calls to the server in a single method call. 

In a Nutshell
Partitioning software into logical tiers makes sense from both software construction and deployment perspectives. From a software perspective, a tiered design allows a single server tier to support multiple client tiers. For example, a business logic tier can service two types of UI tiers. One UI tier is designed to support browser access to the business objects, while the other UI tier might be a Windows UI client. From a deployment perspective, the properly implemented tiered approach provides options for scaled out deployment along tier boundaries.

The tiered approach can only be successful by minimizing points of dependencies between tiers. Communication between inter-tier clients and servers should be chunky, rather than chatty. Information passed between client and server must be of a type which physically marshals between tiers. Always assume a physical separation between tiers. Even if this is not the case at the time of the initial product roll out, this might very well be the case if the software needs to be scaled out at some time in the future.

While stateless, or more to the point, one-call, non-persistent, server components are ideal for providing chunky, inter-tier access, stateful, chatty objects may be freely used on a strictly intra-tier basis.

Although, to be sure, it never pays to get carried away with excessive object oriented design. In a previous article I present an actual case history as an example of what can happen when software which is excessively object oriented is deployed on the server.

www.devx.com/vb2themax/Article/19870

MTS/COM+ deactivation is generally not helpful in minimizing dependencies between clients and server. By all means, deactivation must occur for transactional components, since deactivation is part and parcel of successfully committing a transaction. For other non-transactional, non-poolable objects deactivation serves practically no purpose. Instead, the client should keep its relationship as short as possible by instantiating the server object as late as possible and immediately releasing the object as soon as it is no longer needed.

Nothing can halt a scaled deployment in its tracks faster than machine specific state maintenance. Storing state in the Session object or in any other machine specific repository will lock all requests to a particular server for the duration of the session. When necessary, state should either be delegated back to the persistent client (e.g. via cookies for a browser client) or stored server side in an independent location which is accessible from any machine in the server farm.

By following these guidelines, you can produce software systems which are not only efficient, but which will also be capable of scaling out to support a growing user community and increasing levels of demand.



If you'd like to download this article in Acrobat PDF format, please stop by my Web site at www.FPSNow.com and follow the links to my FTP server to download Scalability.PDF.



Joseph Geretz is the founder of Focal Point Solutions, Inc., a consulting firm serving clients in the New York metropolitan area. He has been working with Microsoft technologies, developing with VB and COM, since 1994 (back then they called it OLE). His primary focus these days is on N-Tier systems, using Microsoft DNA tools and methodologies. His development environments of choice these days are Visual Basic 6 and Visual Studio .Net, although he has fond memories of COBOL, from an earlier, more stateless era.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date