topics: functional programming, concurrency, web-development, REST, dynamic languages

Monday, February 18, 2008

Designing client/server web-applications

This particular entry will be the first in a series concerning some recent thoughts I've had about designing so-called 'rich' web-applications, which I will be thinking of as any distributed client/server application that have the following properties:

  • client and server communicate using HTTP
  • client is developed in JavaScript (GUI is made using CSS+HTML) and runs in an ("A-grade") browser

The series will cover a range of topics which should span all aspects of developing such an application. At present the topics are:

  • Patterns for client application design using Ext JS [JavaScript namespacing, module patterns, inheritance in JavaScript, Model-View-Controller in web clients, Observer pattern]
  • file organization, the build process and proper serving [splitting and building JavaScript programs, static checking, automated unit testing, JS 'compression', building and serving]
  • the RESTful server [designing RESTful backends to JS clients, Ruby/Rail implementation]


Throughout the series I will be developing a mini application called TriBook. The application is for booking meeting rooms at Trifork; it is developed using the JavaScript library Ext JS and Ruby on Rails for the server.

Part I, Section (i): JavaScript namespacing.

The WHAT:

Everyone knows that variables declared at the top level are global, e.g.,

function trim(str) {
return str.replace(/^\s+|\s+$/g, "");
}

is globally accessible using the name trim. When a page is including scripts from different sources that the page itself may not have control of, there is a chance of one script overriding the value of another's variables (the value of PI may indeed change!). Namespacing together with naming conventions significantly reduce the probability of such unintended script interactions.

The namespacing conventions I will be following attempt to mimic packages in Java. A package name is a name of the form: x.y.z... with "x" the top level name of an organization, "y" the organization's domain and then one or more subdomains or application identifiers. Package names are lowercase characters. In the example application, I will be defining JavaScript objects that "live" in the following package: com.trifork.tribook.

Now, "hold on!" you may say, continuing: "JavaScript doesn't have namespaces or packages". While the is true, we can achieve something close using just objects. Many JavaScript libraries support some sort of a namespace function. For instance, Ext JS has the Ext.namespace function. The statement

Ext.namespace('com.trifork.tribook');

ensures that there exists a global variable com which references an object that has a property 'trifork', which in turn has a property 'tribook'. So you can write

Ext.namespace('com.trifork.tribook');
com.trifork.tribook.Application = { ... };

However, while many libraries have the 'namespace' function (or something similar), none that I am aware of have any additional support for actually using namespaces in JavaScript programs. (And this should be where this post hopefully gets interesting and new).

Supposing you are structuring your application by a kind of Model-View-Controller pattern [one way to do this will be the topic of a later posting!]. Naturally you are using namespacing, and you want an application structure with model, view and controller in district packages. In our example, we will have a package structure


com.
trifork.
tribook.
model
view
controller


For example, the package com.trifork.tribook.model will contain a singleton object Model which contains the domain model of our application. We will develop our own namespace function which lets us define this layout in one statement:

namespace({
com:{
trifork:{
tribook:['model',
'view',
'controller']
}
}
});

Our namespace function is polymorphic in the sense that one can call it with one of several input types:

namespace('com.trifork.tribook');

namespace(['com.trifork.tribook.model','com.trifork.tribook.view',
'com.trifork.tribook.controller']);


and the form shown above. It is important to note that namespace('x.y') ensures that x.y exists: if it does not exist, it is created, and if it already exists, it is left alone (implying that namespace is idempotent).

The WHY?

We'll get back to the namespace function later. Here we continue with another function: using. This function lets us write code like:

namespace('com.trifork.tribook.model');

using(com.trifork.tribook.model).run(function(m){

m.Room = function(room_name) {
this.name = room_name;
...
};
});


The pattern: using(exp).run(function(n){...}); applies the inner function (function(n){...}) to the result of evaluating the expression exp.

In our example above, the code defines a constructor function for the Room domain objects; this function is accessed as com.trifork.tribook.model.Room (similarly to what one would do in a Java packaged world).

Now, as we shall see in the next couple of paragraphs, there are several benefits to structuring code this way:

(i) As with all namespacing: we don't clutter the global object and we reduce probability of unintended collisions.

(ii) using can take several parameters and introduces short names for deep namespaces.

namespace('long.boring.namespace.deep.as.well.controller');
using(long.boring.namespace.deep.as.well.model,
long.boring.namespace.deep.as.well.view).run(function(model,view) {

...
});

Not only is it easier to write model than: long.boring.namespace.deep.as.well.model; it is also more efficient.

(iii) If every JavaScript file in your filesystem organization of your application [and this will be a topic of a later posting] has the form:

namespace('xxx.yyy.zzz');

using(xxx.yyy.a,xxx.yyy.zzz).run(function(a,z){...});

Then two things are immediately clear to anyone reading the code (perhaps for the first time): (1) this file defines objects that live in xxx.yyy.zzz, and (2) the objects in this file depend on objects in the packages xxx.yyy.a and xxx.yyy.zzz. While you may not appreciate this immediately, I do believe that as JavaScript applications are getting larger and larger, we need all the help we can get in organizing the application (and I really think this benefit is useful).

(iv) private variables for free. In the statement:

namespace('x.y.controller');

using(x.y.model,x.y.view,x.y.controller).run(function(model,view,ctrl){

function trim(str) {
return str.replace(/^\s+|\s+$/g, "");
}
var PI = 3.14159;

ctrl.Controller = {
init: function(url) {
model.Model.url = trim(url);

},
getPI: function(){return PI;}
};

});
The function trim is private to the code defined within the function being "run". Hence the global namespace is not cluttered with functions that are intended to be used only locally. Also, no other script code can ever accidentally redefine PI, which sounds like a good thing ;-) (One may say that this structure lets one implement some of Douglas Crockfords patterns in a readable way!)


The HOW?
Ironically, when implementing namespace-using, I've decided not to use namespacing! The reason for this is that while

Trifork.namespace('x.y');

Trifork.using(x.y).run(function(y){...});

reduces probability of collision, the non-namespaced version feels much more like a language extension; in:

namespace('x.y');

using(x.y).run(function(y){...});

It almost feels as though 'namespace' and 'using' are language keywords and not user-defined functions. It is like working in an extended JavaScript language that has packages and imports -- although I would have preferred something like:

namespace x.y.z;

import x.y as y and y.z, y.w in function(y,z,w){
...
}


Implementing using is almost trivial:
using(exp).run(function(v){..}) is (almost) equivalent to (function(v){..})(exp). The full implementation is:

function using() {
var args = arguments;
return {run:function(inner){return inner.apply(args[0],args);}};
}

(not that when running the inner function, 'this' is bound to the first argument of using).

The namespace function is more code, but relatively straightforward:

//Update: generalize array case to handle a mix of strings and objects via recursion.
//Update [June 11, 2008] Simplifications noticed by Aaron Gray, thx.

function namespace(spec,context) {
var validIdentifier = /^(?:[a-zA-Z_]\w*[.])*[a-zA-Z_]\w*$/,
i,N;
context = context || window;
spec = spec.valueOf();
if (typeof spec === 'object') {
if (typeof spec.length === 'number') {//assume an array-like object
for (i=0,N=spec.length;i<N;i++) {
namespace(spec[i],context);
}
}
else {//spec is a specification object e.g, {com: {trifork: ['model,view']}}
for (i in spec) if (spec.hasOwnProperty(i)) {
context[i] = context[i] || {};
namespace(spec[i], context[i]);//recursively descend tree
}
}
} else if (typeof spec === 'string') {
(function handleStringCase(){
var parts;
if (!validIdentifier.test(spec)) {
throw new Error('"'+spec+'" is not a valid name for a package.');
}
parts = spec.split('.');
for (i=0,N=parts.length;i<N;i++) {
spec = parts[i];
context[spec] = context[spec] || {};
context = context[spec];
}
})();
}
else {
throw new TypeError();
}
}

13 comments:

Aaron Gray said...

I am wondering about the efficiency of the namespace function with big libraries which it is really designed for as compared to explicit namespace declarations ?

krukow said...

Hello Aaron.

I haven't done any performance evaluations of the function, but I would argue that the namespace function is at least as efficient in terms of space and time as manual namespace creation:

In terms of space, the same number of objects are created so there is no difference.

In terms of time, the algorithm is simply doing what you would do manually, i.e., check if the package object already exists and if not create it.

I have used these functions in a large scale JavaScript project with success.

Cheers,
- Karl

Anonymous said...

Hi,

nice function, I like the idea of this at the top of JS files, especially when declaring static modules. I was wondering what sort of licence the code has on it? Is this open source?Many thanks for your other thoughts on Functional and Object Orientated programming in JS, it truly is a flexible language!

krukow said...

@Andy,

You are free to use it as you like, all I ask is that you provide the URI to this blog posting in the source.

Cheers,
- Karl

Anonymous said...

Karl,

Great pair of functions, really useful.

I've bundled it with some documentation:

http://appengine.bravo9.com/b9j/documentation/namespace.html | b9j

You can download the pack here:
http://appengine.bravo9.com/b9j/b9j-latest.zip

krukow said...

Hello Rob,

Thanks for your comment.

I've moved this blog to

http://blog.higher-order.net/2008/02/18/designing-clientserver-web-applications/

Please write your comments there.

Juan Mendes said...

Are there any benefits of using() instead of just an anonymous block?

Like:

Ext.ns('com.wfs.magic');

(function() {
var magic = com.wfs.magic;
magic.anyMethod();
})();

krukow said...

Please post comments on the new blog:

http://blog.higher-order.net/2008/02/18/designing-clientserver-web-applications/

deodorel said...

There is a framework which gets pretty close to what you want: dojo. Check it out, it really changed the way i looked/worked at javascript applications.

krukow said...

Please post comments on the new blog:

http://blog.higher-order.net/2008/02/18/designing-clientserver-web-applications/

Anonymous said...

Thank you very much for your kind words (and an excellent blog post)!
web template

Michelle C. said...

Thanks for the post. design web applications could really help in Designing your business website.

Unknown said...

Hi,if your website is an e-commerce website, make the purchasing bag or seek the services of someone to do it for Web Design Cochin. Moreover, clients need their personal and financial details to remain private. As a result, it is essential to consist of security on the website.Thanks...