Coding

Developing a complete client-server application with qooxdoo on Cloud9IDE. Part 1: Application architecture

Having found out that programming with the qooxdoo (qx) framework on the Cloud9-IDE (C9) was not only possible, but actually quite pleasant, I want to now continue by putting together a node-js based architecture that can be the basis of actual applications. In this post and the posts to follow, I will document the different choices of libraries and my experience with them. The current state of the entire application is available on GitHub ]

The frontend is clear — qooxdoo — but what needs to be chosen are three things: 1) the communication layer that lets the frontend and backend talk to each other, 2) the nodejs modules that help me create a maintanable and scalable backend, 3) the persistence layer that allows to save the application data in a -yet to be chosen – database system.

Points 1 and 3 will be covered in later posts. For now, it is enough to say that all my communication needs will be taken care of by  socket.io. In this post, I want to deal with application architecture on the backend.

Application architecture: using C9’s own “Architect”

One of the most important decisions, one that is later hard to change, concerns the application architecture, i.e., how to modularize the code and decide what part of the code does what. When I was coding with PHP, the buzz word was MVC (Model-View-Controller) and this design patterns could be very well translated into the way PHP works. A dispatcher script would call a controller class to query data from the backend into models, and populate a view with the data. If you like this paradigm, there is backbone.js, which is a kind of javascrit MVC framework that many people use, or many others.

MVC and its various siblings is certainly a good pattern when thinking about data. I think, however, that is doesn’t provide an architectural basis for developing data-driven, asynchronous javascript applications. The problem MVC solves (separation of data and presentation) is not a real problem in client-server applications. “Views” are not needed, because data and presentation are naturally separated: all the frontend needs is raw data with which to populate its widgets. But most importantly: the MVC pattern provides no answer to javascript applications’ most important problem, which is the asynchronous nature of javascript.

For some time, I was intrigued by Nicolas C. Zakas Scalable JavaScript Application Architecture, which combined modularity with safety concerns by strictly separating 1) a application “core” which provided all the basic functionality such as client communication, database access, etc., 2) modules, which implemented all the functionality of the application but had no direct access to the core and 3) a “sandbox” which exposed the resources of the core to the modules in a controlled manner. One feature of this approach is that it doesn’t need any library or framework, but can be simply coded by hand – in fact, it must be coded by hand, because the isolation of core and modules is achieved by using closures. This doesn’t work very well with qooxdoo and its traditional OO-approach where one file contains one class with methods, and those classes cannot easily be put into closures. But the main reason I didn’t use this approach was that I wanted some kind of framework that provided me a little more magic.

This is why I was happy to discover that C9 itself was written using a architectural framework named – fittingly – Architect. The idea is that each and every functionality of an application is written as a plugin which registers itself with the application and exposes its functionality to the application without having direct access to the other plugins. There is no sandbox. Instead, each plugin exports an API and “consumes” the API of other plugins. The idea of making everything a plugin with a small but concise API is very powerful. Also, if all functionality of a typical web application backend is modularized into small and focused plugins, it becomes very easy to reuse these modules in a different application. The library also deals with the problem that the initialization of plugins is asynchronous, and it deals with application configuration, which is a very important aspect that cannot be solved at the plugin level.

One caveat: Architect isn’t very well documented yet, seems to have changed considerably before it was released, and there seem to be very different ways of using it. I had to experiment quite a bit before I got things to work. But, as C9 itself, the project seems to hold a lot of promise and just “feels” right, so I was happy to do a little trial & error.

To use Architect, you’ll need to install it (npm install architect) and then create a plugin for each functionality that your application will need.  C9’s github page contains several architect-related repositories which provide examples how to use the framework. There is one calculator demo that is worth looking at, even though I didn’t get it to work. The following code assumes that you have set up a sample qooxdoo project in C9 as described in the the intial tutorial.

The main configuration

Replace the server.js file with the following code, which is from here:

var path = require('path');
var architect = require("architect");

var configName = process.argv[2] || "build";
var configPath = path.resolve("./configs/", configName);
var config     = architect.loadConfig(configPath);

architect.createApp(config, function (err, app) {
    if (err) {
        console.error("While starting the '%s' setup:", configName);
        throw err;
    }
    console.log("Started '%s'!", configName);
});

The calculator demo’s server.js shows that there is a much simpler approach, but this setup allows several configs (for example, for “source” and “build” versions of the qooxdoo app) that can be run from the same file.

The server will try to read a configuration file from the “configs” directory, which we will need to create, and in which to put the following “build.js” file:

// build.js
var path = require("path");

module.exports = [
  { packagePath: "../plugins/http", root : path.resolve("testapp") }
];

The README explains:

Notice that the config is a list of plugin config options. If the only option in the config is packagePath, then a string can be used in place of the object. If you want to pass other options to the plugin when it’s being created, you can put arbitrary properties here.

Even the connect server, which usually is in a central place, should be wrapped in a plugin. This is what we do now. In the (newly created) “plugins” directory, we create a new folder “http” with two files:
package.json

// package.json
{
    "name": "http",
    "version": "0.0.1",
    "main": "http.js",
    "private": true,

    "plugin": {
        "provides": ["http"]
    }
}

http.js (adapted from here)

// This plugin gives provides a connect server for the application

// @options is the object in the config.js file for this plugin.
//   @options.port is the port to listen on.
//   @options.host is the host to bind to
//   @options.root is the directory from which to serve static files
// @imports is the various services that this plugin declared as dependencies
//   This plugin doesn't have any
// @register is a callback function expecting (err, plugin) where plugin is the
// provided services and lifecycle hooks.  This plugin exports "http".

module.exports = function setup(options, imports, register)
{
    // dependencies
    var connect = require("connect");
    var assert  = require("assert");
    var path    = require("path");

    // options/parameters
    var host = options.host || process.env.IP;
    var port = options.port || process.env.PORT;
    var root = options.root;
    assert(root && typeof root=="string", "You must provide a document root for the http server");

    // create server and register with architect when done
    var app = connect().use(connect.static(root));
    var server = app.listen(port, host, function (err) {
        if (err) return register(err);
        console.log("Connect server listening on http://%s:%s, serving %s", host, port, root);
        register(null, {
            // When a plugin is unloaded, it's onDestruct function will be called if there is one.
            onDestruct: function (callback) {
                server.close(callback);
            },
            // API
            http: {
                server : server
            }
        });
    });
}

When you start the server (open server.js as active tab & press “run” or “debug”), you should be able to open the default qooxdoo app at http://PROJECT.USER.c9.io/build/index.html. Ignore the IP/Port that is reported on the console – this information is only relevant when you deploy the finished project to your own server.

To run a “source” version of the qx app, you’ll have to create a file named source.js in the “configs” dir with the following content:

//source.js
var path = require("path");
module.exports = [
  { packagePath: "../plugins/http", root : path.resolve(".") }
];

and then open the application from

http://PROJECT.USER.c9.io/testapp/source/index.html.

You get the idea. As said before, this setup should never be used on a production server, since it exposes your entire source tree.

The “http” plugin is a good example to demonstrate how smart this form of modularization is. Should you decide to later exchange the connect server with a different server (which, of course, needs to have the same API), you can do this without any changes to the rest of the application. Step by step, we can now continue hooking in the other parts of the application. The next post will deal with client-server communication and the installation of socket.io.

Advertisements

4 thoughts on “Developing a complete client-server application with qooxdoo on Cloud9IDE. Part 1: Application architecture

  1. I tried this stuff and converted a project (qooxdoo – node.js – firebird) to architect. This costs me 2 Days, but it was worth it. Without your blog post i never had realized how useful architect is.

    Thank you very, very much!

    with best regards
    Michael

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s