Thin Client Coding Tips

Thin-client hardware and architectures have been discussed on this site. Related topics include Reasons To Use Two-Tier Fat Client Instead of Browser-Based Application Development Technology, JDBC Brings the Fat Client to Java, and IE4: The First Fat Client Browser. Still, I frequently get questions along the lines of "How do I write a thin client application?" or "How do I know if my application is a thin client?". This brief provides some thin client coding tips.

Do not be fooled into thinking that these tips have no bearing on your work if you are not writing thin client software today. Even if you are deploying two-tier, or even single-tier, applications, many of these tips will help you produce better software.

How Thin is Thin or Thinness is In the Eye of the Beholder

Before giving tips, however, I must reiterate that my idea of thin may not agree with your idea of thin, and that my idea of thin may not be appropriate for your application or deployment environment.

Thin clients are often discussed in the context of three-tier architectures. In the typical simple description of three-tier, the tiers are labeled Presentation, Business and Data Access, or some trivial variation thereof. So, what does it mean to write the Presentation layer? Well, the code should be responsible two things:

  • User Interface Appearance. This is what the user sees: windows, buttons, text boxes, graphics, sliders, tabs, etc. In environments such as Visual Basic, this is mostly programmed declaratively with the help of a visual builder. Other languages like Java, Delphi and C++ require procedural programming, although development environments provide builders and wizards to hide the code to whatever degree the programmer desires.
  • User Interface Behavior. This describes the changes in what the user sees. For example, an OK button may be grayed as long as a required text input remains empty, then become active when data is entered. Because this change involves only the UI and results purely from some change in the UI, it is part of the Presentation.

The presentation layer can also be bounded by what it does not do:

  • Business Logic. Any calculation based on business data is business logic. Any conditional programming construct based on business data is business logic. Business logic does not belong in the presentation layer, even if it is something as simple as showing an invoice as the sum of the invoice lines.
  • Business Process. A sequence of actions such as saving an invoice, reducing inventory, creating a packing list and creating a shipment order is a business process. Such a sequence should not be coded into the Presentation layer.
  • Database Transactions. Database transaction logic groups actions that must all be applied or none be applied. This should not be in the Presentation layer.
  • Database I/O. Database actions, like SELECT and INSERT, should not be in the Presentation layer. This directly contradicts the common 2-tier programming model for a tool like Visual Basic, in which each form (part of the Presentation layer) contains one or more Data Controls that represent database connections.

Why Thin May Be Useful

I have argued that 2-tier, fat-client architectures are useful and have their place. This is not to claim the absence of usefulness for thin clients. Among the desirable attributes of thin-client architecture are
  • Functional adaptability. I do not argue when Bertrand Meyer claims one should use O-O to produce adaptable software. I also do not argue with Steve McConnell when he labels O-O an "expert technology". Pragmatically, the adaptability gained by a disciplined separation of Presentation from Business and Data Access layers probably yields more bang-for-the-buck across a large cross-section of development teams than full-blown O-O formalism. If you have dutifully separated a business process or database transaction from the 15 forms from which it is used, you will one day be able to make a change in that process by changing one piece of code.
  • Deployment adaptability. Once you have produced a thin-client application that, say, uses HTML and ECMAScript for the user interface, producing a variation of the same application that uses Java or Visual Basic for the client is reasonably easy and takes must less effort than to do the same for a fat client implementation.
  • Ease of client installation and configuration management. For browser-based thin-clients, configuration is done for all thin applications once the browser is installed. For other thin clients, there should be fewer files that need to be installed, along with fewer registry, INI and/or configuration file settings that need to be made.
  • Ease of application configuration management. The client portion of an application may run on 10,000 desktops. The server portion may run on one or a few servers. A fat-client application may require 100 settings on each client, or one million total settings to maintain. A thin-client implementation may move 95 of those settings to the server. This leaves 50,095 settings to maintain. The latter number makes me more comfortable.
  • Just-in-time deployment. Browser-based thin clients download the client when the user needs it. No effort is wasted installing software the user might need.

Even if you don't write thin clients, applying thin client discipline will allow you to produce more adaptable software.

Finally, the Tips

OK. You've humored me by reading all the preliminaries. Here are the tips, in no particular order. (Sorry Visual Basic haters, but code snippets are in VB.)

A UI event handler can have as many UI operations as you want

Use event handlers to provide a UI richness that enhances the user's experience. Update status bars, gray and ungray buttons, animate graphics, set colors, you name it. Your user interface will benefit from a speed optimization if the Presentation layer is separated from the Business and Data Access layers by a network, especially if it is a slow one.

I especially believe in disabling and enabling controls based on whether they are valid. For example, a Save command button should not be enabled if a required field is empty. Therefore, I use code like the following.


    Private Sub Form_Load()
        SetCommandStatus
    End Sub

    Private Sub txtRequired_Change()
        SetCommandStatus
    End Sub

    Private Sub SetCommandStatus()
        ' This would usually be more complex logic
        cmdSave.Enabled txtRequired.Text <> ""
    End Sub

Back to List

A UI event handler can have either 0 or 1 non-UI operation

Business or Data Access code may reside on different machine than the Presentation code. At the very least, then, we want to minimize calls from the UI code to the rest of the application.

I believe in limiting the number of calls strictly to one because any sequence of calls is probably a business rule or business process in disguise. For example, the following code is executed when a new item is added to an invoice form.


    Private Sub DBGrid1_UnboundAddData(ByVal RowBuf As MSDBGrid.RowBuffer, _
                                       NewRowBookmark As Variant)
        Dim curItemPrice As Currency
        Dim lngItemMult As Long
        Dim objItem As New Item

        On Error GoTo UAD_Error
        lngItemMult = RowBuf.Value(0, 0)
        objItem.Key = RowBuf.Value(0, 1)
        curItemPrice = objItem.GetPrice(m_oCustomer, lngItemMult)
        m_oInvoice.AddItem objItem, lngItemMult, curItemPrice
        NewRowBookmark = objItem.Key
        Exit Sub

    UAD_Error:
        MsgBox Err.Source & vbCrLf & Err.Number & ": " & Err.Description
        RowBuffer.RowCount = 0
        Err.Clear
    End Sub
This is a pretty simple routine, but it encapsulates both a business rule (the price may be customer- and quantity-specific) and some implementation details (an item knows its price, the row's bookmark is the item key). The rule and implementation details must exist somewhere in the code, but an event handler is not the right place, for at least two reasons. First, the rule and details are likely to change, and one reason to use a thin-client architecture is to minimize the impact of changes on the configuration management of the client. Second, the rule and details may well be needed on more than one form. If they are re-used via cut-and-paste, a maintenance nightmare slowly builds.

I prefer an event handler like


    Private Sub DBGrid1_UnboundAddData(ByVal RowBuf As MSDBGrid.RowBuffer, _
                                       NewRowBookmark As Variant)
        RowBuffer.RowCount = m_oInvoiceEngine.AddItem(RowBuf.Value(0, 0), _
                                                      RowBuf.Value(1, 0), _
                                                      m_oCustomer, _
                                                      NewRowBookmark)
    End Sub

Back to List

Do not cheat the previous tip by creating a "partner" class or module for each form (window)

I've actually seen this coding style. A form called InvoiceForm gets initialized with a reference to an instance of InvoiceFormObject. The code for each event on the form is one line, such as

    Private Sub Button1_Click()
        m_oInvoiceFormObject.Clicked
    End Sub
Now the form no longer has any references to business objects or database access; this is all in the other object. While this is a step in the right direction compared to "typical" fat client coding techniques, there are some serious flaws.

First, there is the temptation to give InvoiceFormObject a reference to InvoiceForm or one or more controls on the form. Since InvoiceFormObject has business and/or database logic, it is not part of the Presentation layer. Since one of the goals of n-tier architecture is to be able to change the hardware platform on which each tier resides, we would want to be able to move InvoiceFormObject to a remote machine. To do this, we would have to now be able to pass references to the form and/or controls across the network. Even if we technically can, performance certainly can suffer.

This is good general advice: do not pass references to UI objects to the other layers. Instead, pass values from the UI objects to the other layers, and set values of UI objects with values returned from other layers. For example,


    Private Sub Button1_Click()
        Dim curPrice As Currency
        Dim strDesc As String

        m_oItemLookup txtItemKey.Text, strDesc, curPrice
        txtDesc.Text = strDesc
        txtPrice.Text = CStr(curPrice)
    End Sub

A second and larger flaw is that the code in InvoiceFormObject may be needed for several forms. We do not want to have code duplicated multiple times through cutting and pasting. Logic related to a particular business process should be written once and shared by all forms that require it.

Back to List

A form (window) cannot be bound to a database in any way

Pretend the Data Control does not exist. All controls are unbound. Yuck. The Data Control (or its equivalent in non-VB development environments) and bound controls, along with visual GUI and query builders, are the foundation of RAD as we know it in the fat client world. It is incredibly painful to return to all the procedural coding required to connect a database to a user interface, but that coding is currently necessary in the general thin client world today. (I say "general" because there are some decent tools, such as Cold Fusion Studio and Visual InterDev, for producing HTML clients without directly writing volumes of procedural code, but the process is not as fluid or visually rich as for building fat clients with Visual Basic, Delphi or PowerBuilder. It also so happens that, although Cold Fusion Studio and Visual InterDev produce applications that run with thin (browser) clients, it is not a fun exercise to try to re-work the code for a non-browser thin client. The UI code is totally interwoven with the data access code.)

My personal wish is that environments soon include controls that can be bound to properties of arbitrary object instances, or perhaps even output parameters or return values of methods of said instances. Then the user interface could be declaratively connected to business process or business logic objects. This is a step beyond current Smalltalk and Java environments (Visual Age, PARTS, Visual Cafe) that allow procedural code to be generated for such links in a wizard-like way by interacting with the UI at design time.

Back to List

Separate business processes from other business rules and logic

Since all business logic resides outside the Presentation layer, you may wonder what this has to do with writing thin-client code? I'm not certain it does. I do, however, think this is an important topic.

Many (but not all) object-oriented software engineering experts that I admire recommend never creating separate classes for processes. They argue that, for example, an instance of a Product class should be able to re-order itself, or that an Invoice should be able to save itself and arrange for picking and shipment.

Well, I have gone this route more than once and never liked where it led me. Simple processes typically worked out fine. More complex processes, however, require the services of many, many different classes. The class chosen to orchestrate the process becomes coupled to many otherwise unrelated classes. Likewise, the class assumes many responsibilities that are more clearly related to the process than the concept being modeled. Using separate classes to represent processes yields lightly coupled classes that model business entities, and heavily coupled classes that model processes. Since processes change more frequently than simple rules or attributes associated with business objects, most of the on-going software maintenance is concentrated on the process classes.

Translated from the lower level of classes to the higher level of architecture service layers, similar advantages apply. Changes to processes affect only the Business services that implement processes. Further, the process services provide a layer of insulation between the UI and the business objects.

Back to List


Copyright © 1998 Scott Nichol.
11-May-98