AjaxPatterns
This is the original book draft version for the Fat Client Ajax pattern, offered for historical purposes and as an anti-spam measure.
You're probably better off visiting the Live Wiki Version for Fat Client instead, where you can make changes and see what others have written.

Fat Client

BrowserSide, Decoupled, DHTML, Fast, Responsive, Rich, Thick

Developer Story

Dave's new mortgage application is very quick as it avoids server calls wherever possible. After some benchmarking, he discovered that optimised Javascript is actually fast enough to run the financial algorithms in the browser, so that users now receive almost immediate feedback as they tweak parameters.

Problem

How can you architect the system to feel truly rich and interactive?

Forces

  • The application should respond to user actions quickly, ideally instantaneously.

  • Responses from the server can be noticeably latent due to data transfer and server processing overheads.

Solution

Create a responsive Ajax App by performing remote calls only when there is no way to achieve the same effect in the browser. Whereas the server is traditionally the realm of more complex processing, this pattern suggests harnessing the browser for all it's worth. In the extreme case, it means running just about everything inside the browser, but that's not the main message here. The main message is to reconsider the traditional server-centric view of web architecture; for developers to be more conscious about decisions on matters regarding where the various components live. Placing more logic inside the browser gives you the opportunity to make the application more responsive.

Web apps usually have several layers:

  • User interface

  • Application logic

  • Business logic

  • Data access

  • External system communication

The user interface is what the user sees and interacts with. The application logic concerns the dialogue between the user and the computer, the flow of information back and forth: Given a particular user action, what should the computer do next? The business logic concerns knowledge about the domain and the website owner's practices and policies. The data access layer concerns reading and writing data to a persistent store. External system communication is also necessary in many enterprise systems, where outside systems play the role of information sources and sinks.

Conventional web apps concentrate UI code in the browser, using a mix of HTML and CSS, with some Javascript used to perform a few basic UI tricks like placing keyboard focus or showing some animation or offering a dynamic menu. Everything else is usually managed on the server. This pattern reconsiders the balance by suggesting that some applications are better served pushing application logic and business logic into the browser (Figure 1.43, “Responsibilities of a Fat Client”).

Figure 1.43. Responsibilities of a Fat Client

Responsibilities of a Fat Client

With application logic in the browser, an inversion of control occurs in which the browser now controls the flow of activity. When the user performs some action, it is the browser that decides how to respond, and calls the server only if necessary, and for very specific services.

The advice to hold business logic in the browser is anathema to most literature on serious website development, and should not be taken lightly. Business logic in the browser has numerous problems:

  • Programming in Javascript can be difficult because many features familiar to mainstream developers are unavailable. The standard API is minimal, object-oriented facilities are based on prototypes rather than classes. Indeed, the usage of dynamic typing is also uncomfortable for many developers in the Java and C# camps.

  • Portability is a further complication due to the number of platforms that must be supported, and their subtly inconsistent behaviours.

  • Development is also a challenge. Coding, refactoring, debugging, automated testing, monitoring, are standard in modern IDEs like IntelliJ Idea, but support at this level is minimal for Javascript.

  • Security is another constraint which forces logic server-side. Savvy users can peruse code and data in the browser and can even tamper with what's sent back to the server. Obfuscation - making the code difficult to read - is a very weak defence.

  • Business and application logic should be held on the server, where they can be reused by other clients.

  • Web data generally can't be saved on the local machine.

Are any of these showstoppers? Let's look at how we can cope with each of these problems:

  • Javascript may be different to server-side languages, but many programmers are discovering it's a lot more powerful than previously assumed. Until recently, the main resources on Javascript were "cut-and-paste this code" websites, but there are an increasing number of books and resources that take the language seriously. The language has undergone a renaissance in parallel with Ajax. New idioms and patterns are emerging, along with a pile of cross-browser frameworks to augment the relatively bare native API. The recent introduction of JSAN - a repository of Javascript libraries like Perl's CPAN - will only further reuse. Object-oriented concepts, including inheritance and aggregation, are indeed possible, if not in a manner familiar to all server-side developers. Dynamic versus static typing is a matter of taste which shouldn't affect a language's credibility; the dynamic approach of Javascript may not be everyone's preference, but Javascript is hardly an outsider here.

  • Browsers are more standard nowadays, and many of the new libraries abstract away browser dependencies (see “Cross-Browser Component”). For example, over a dozen libraries offer a browser-independent factory method to retrieve XMLHttpRequest.

  • Recent tools are making life a lot easier, some discussed in the ??? section.

  • Security remains an issue, but it's possible to deploy the application so that security-critical functionality can be exposed server-side in well-defined services, with the bulk of the application logic held locally and calling on those services as required. There might be duplication because browser-side validation is also important for quick feedback, using Javascript to create a screening test before passing a request to the server. However, there are ways to deal with such duplication if it's a serious issue. For instance, with server-side Javascript, you can have the same script running in the browser and the server. Or by using some custom, declarative, notation to capture business rules and have processors for it on both sides.

  • It's true that certain business logic needs to be made available to multiple clients and therefore needs to exposed as “Web Service”s, in which case there's a good argument for keeping it inside the server. (It could also be executed in the server as well as the browser, though that's a fairly unusual practice.) Often, though, business logic is application-specific, and that's even more the case for application logic (notwithstanding grandiose plans for running the same app on mobile phones and desktops).

  • Browsers cannot normally persist data locally, but a background thread can be used to periodically synchronise with the server. In the 1990s, many desktop applications forced the user to explicitly save their work, whereas many of them now save periodically and save upon the user quitting. In the same way, it's now feasible for web apps to persist data transparently, without affecting workflow. If local persistence is critical enough to break away from "pure Ajax", some easily deployed Flash libraries are available (e.g. Brad Neuberg's AMASS).

None of this says that programming in Javascript is more productive than programming in Java or C# or Ruby. In most cases, it isn't and never will be. Serious Javascript programming is nonetheless a very real proposition. Professional software development involves choosing the language that fits the task at hand. Given the many benefits of browser-side coding, there are at least some circumstances which make Javascript the language of choice for implementation of business and application logic.

Decisions

How will business and application logic be implemented in the browser?

Conventional applications use Javascript for a little decoration. Even when a lot of Javascript is used, it's often packed into tightly-scoped library routines, to perform little tricks like validating a form's fields match regular expressions, or popping up a calendar. These are just little detours on the well-travelled road between browser and server. In contrast, a Fat Client responds to many events itself, delegating to the server only if and when it deems necessary.

All this implies a very different usage of Javascript, and there are some techniques which can help. It's beyond the scope of this pattern to cover them in detail, but here are a few pointers:

  • It's easy to fall into the trap of "paving the cow paths"[12] by applying design styles from other languages to Javascript. It's usually more productive to embrace Javascript as a unique language with its own idiosyncratic features and usage patterns, and be wary of applying architectural concepts from your favourite server-side language if they don't seem to fit in.

  • Javascript uses a prototype-based paradigm, which is worth learning about. Object-oriented concepts like inheritance are certainly possible, but it's important to appreciate how they fit in to the prototype model.

  • Development and deployment practices apply as much to a rich browser application is as important as testing the server code, as discussed in ???.

  • Javascript runs slower than equivalent desktop applications, so optimisation is important. Also, consider issues memory usage and be aware of browser-specific bugs which might lead to memory leaks.

  • Break complex applications into multiple Javascript files and consider managing them with “On-Demand Javascript”.

  • Reuse, Reuse, Reuse. As the ??? appendix shows, there's a proliferation of Javascript frameworks and libraries becoming available.

Real-World Examples

NumSum

NumSum is an Ajax spreadsheet that provides all the basic spreadsheet functionality in Javascript: insertion and deletion of rows and columns; various formulas; user can enter values into any cell; text-formatting (bold, italic, underline a cell); text-justification; Embedded links and images.

The basic spreadsheet is implemented as a table. Table cells aren't actually editable, so the script needs to simulate an editable table. An event handler ensures a cell becomes active when the user clicks on it, and that cell is then morphed to reflect subsequent user input.

GMail

GMail, like several of its competitors, presents a rich, Ajax, interface to a mail system. Conventional webmail offerings rely heavily on form submissions and page reloads - a new page must be opened each time the user wants to start composing an email, finish composing an email, open up an email message. With GMail, the list of emails is always present and regularly refreshed. Partial edits are periodically saved as well. All of these activities are handled within the browser using a combination of ??? and ???.

DHTML Lemmings

DHTML Lemmings shows how much can be achieved with some clever Javascript hacking (Figure 1.44, “DHTML Lemmings”). It's a full-featured implementation of the Lemmings PC game utilising a “Richer Plugin” only for sound - the entire game runs on DHTML, DOM, and CSS. The gameplay consists of a static image for the current level, with “Sprite”s used to render the lemmings and other objects in the game. There is a periodic timer continuously checking the current state of each object, and DOM manipulation of the objects' properties is used to render their states at any time.

Figure 1.44. DHTML Lemmings

DHTML Lemmings

JS/UIX Shell

JS/UIX shell is a demo of an in-browser Unix shell (Figure 1.45, “JS/UIX Shell”). In its current form, it's a demo only. But it's capable of all the basic operating systems commands and even includes a vi editor! The approach stands in contrast to browser-side terminal emulators, which are thin applications that pass characters back and forth. With appropriate server-side integration, the application could be made into a functional multi-user application. Command-line remains the mode of choice for many power users, and this demo shows how an Ajax Fat Client can achieve it.

Figure 1.45. JS/UIX Shell

JS/UIX Shell

Code Example [AjaxPatterns Basic Wiki]

The Wiki Demo is based on Fat Client principles, though its functionality is fairly limited. All of the interaction and rendering occurs locally. The only interaction with the server is a periodic synchronisation event, when the following occurs:

There's a timer to ensure synchronisation occurs after five seconds of inactivity:

    var syncTimer = null;

    function startPeriodicSync() {
      stopPeriodicSync();
      syncTimer = setInterval(synchronise, 5000);
    }

    function stopPeriodicSync() {
      clearInterval(syncTimer);
    }

The timer starts on page load, and is suspended each time the user begins to modify some text:

    window.onload = function() {
      synchronise();
      startPeriodicSync();
    }
   
    function onMessageFocus(event) {
      ...
      stopPeriodicSync();
    }
   
    function onMessageBlur(event) {
      ...
      startPeriodicSync();
    }

The script also contains a variable to accumulate pending messages whenever the user changes something:

    var pendingMessages = new Array();
    ...
    function onMessageBlur(event) {
      ...
      if (message.value != initialMessageContent) {
        pendingMessages[message.id] = true;
      ...
    }

The synchronisation event builds up some XML to post to the server, based on the pendingMessages array that has been accumulated. If no changes are pending, it simply requests the current server state. If there are changes pending, it posts those changes as an XML message. Either way, the server will respond with an XML specification of its current state.

    function synchronise() {
   
      var messageTags = "";
      for (messageId in pendingMessages) {
        var initialServerMessage = $(messageId).serverMessage;
        messageTags += "<message>";
        messageTags += "<id>" + messageId + "</id>";
        messageTags += "<lastAuthor>" + $("authorIdSpec").value + "</lastAuthor>";
        messageTags += "<ranking>" + initialServerMessage.ranking + "</ranking>";
        messageTags += "<content>" + escape($(messageId).value) + "</content>";
        messageTags += "</message>";
   
        $(messageId).style.backgroundColor = "#cccccc";
      }
   
      var changesOccurred = (messageTags!="");
      if (!changesOccurred) {
        ajaxCaller.getXML("content.php?messages", onMessagesLoaded);
        return;
      }
   
      var changeSpec = "<messages>" + messageTags + "</messages>";
      ajaxCaller.postBody
        ("content.php", null, onMessagesLoaded, true, null, "text/xml", changeSpec);
      pendingMessages = new Array();
    }

The main application logic is virtually orthogonal to server synchronisation. The only concession is the accumulation of pending messages, to save uploading the entire browser state each time. The timer and the synchronisation process know about the core wiki logic, but the core wiki knows nothing about them, and can therefore be developed independently. It's easy to imagine how the wiki could evolve into something with much richer browser-side features, like formatted text and draggable messages and so on.

Alternatives

Thin Client

A thin client contains only basic Javascript and frequently refers back to the server for user interaction. This is likely to remain the dominant style for Ajax Apps, and is useful in the following circumstances:

  • It's important to support legacy browsers. Graceful degradation is still possible with Fat Clients, but requires more work and there's the potential for a lot of redundancy.

  • Business logic requires sophisticated programming techniques and libraries that can only be implemented server-side.

  • Business logic involves substantial number-crunching or other processing which can only be performed server-side.

  • Due to bandwidth and memory constraints, the application cannot be held locally.

  • There is complex dialogue required with external services, and due to cross-domain security policies, those services cannot be accessed from the browser.

Desktop Client

A desktop client runs as a standard application in the user's operating system, and connects to one or more servers using HTTP or any other protocol. Compared to a Fat Client, a desktop client can be richer and faster. However, the benefits of a web-enabled application are lost as a result. You can compensate for some of these benefits:

  • Portability can be achieved with a platform-neutral programming platform.

  • Centralised data can be achieved by ensuring all data is retained server-side.

  • Familiar user-interface can be achieved by tailoring to each individual platform's look-and-feel.

In some contexts, a desktop client represents the best of both worlds: rich client backed by a powerful server. In other situations, a Fat Client based on Ajax principles is a more appropriate sweet spot: powerful server, rich-enough client, and easy access through any modern web browser.

Related Patterns

“Periodic Refresh” is a relatively unobtrusive way for the client state to be enriched with any new server-side information.

“Submission Throttling” is a relatively unobtrusive way for the server to be notified of any significant changes that have occurred in the browser.

???

If the user is going to spend a long time in the browser, the various ??? should be adopted to improve the experience.

Since Fat Clients tend to contain bulkier scripts, “On-Demand Javascript” is useful for managing them.

“Drag-And-Drop” is familiar to many desktop users and helps to make a large application feel more natural. Whereas more conventional web clients rely on form-style interaction, Fat Clients can feel more rich if they support “Drag-And-Drop”.

Where a Fat Client is seen as a replacement for a desktop equivalent, “Host-Proof Hosting” might be considered to avoid compromising on the superior security a localised solution sometimes offers.

Metaphor

Agile development methodologies such as Scrum aim for an "inversion-of-control". Workers (like the Fat Client) are empowered to plan their own activities, and management's role (the Server) is to offer whatever services workers request in order to conduct those activities.

Want to Know More?



[12] "Paving the cow paths" refers to the way cows mindlessly follow an old path, continuing to walk around trees that aren't actually there anymore.

Live Wiki Version for Fat Client