Conventions Client API Server API Architecture Getting Started

Introduction

Sapphire is specifically designed for the creation of Single Page Applications (SPA).These applications have a number of special considerations over more traditional web applications, for example, hot loading of parts of the application when they are needed, construction of the application from multiple sources, AJAX service functions to perform backend actions and retrieve updated data, and a front end API that ties it all together.

Sapphire is built in node.js and makes use of a number of 3rd party modules, such as Q for promises, MooTools for more traditional class support, JQuery for DOM Manipulation and the whole thing is built on top of express, giving access to lots of useful middleware. However, routing in sapphire is done very differently than for a typical express application.

The framework is object oriented, on both the server side and the client side. The client side framework uses a Model/View/Controller (MVC) paradigm.The server code is broken into three major areas of functionality.

  1. Building the application to send to the client
  2. Routing to AJAX service functions
  3. Delivering static assets such as images, JavaScript and CSS files.

Sapphire is a robust framework and handles a number of high level functions right out of the box.

These will all be discussed in more detail later.

Goals

This framework was created with a number of goals in mind:

Separation of Skills

There should be a clean divide between application code and the visual design. The framework is designed to allow visual and UX designers who can create HTML and CSS to be active, productive members of the team, freeing up programmers to concentrate on application logic, not pixel pushing. There is also an easy transition for some of this staff to participate in coding if that is their career trajectory.

Also, the browser code should be easily separable from the server code as staff is frequently divided by these different skills.

Locality

Code is organized so that files that support functionality are located close together. This makes working on different aspects of an application easier, and helps reduce dependencies.

MVC on Client

Single page applications have the bulk of the user interaction in within the browser; the majority of the application code will be there. So, in Sapphire, that’s where MVC is implemented. The paradigm of the back end is centered around its specific needs.

Terminology

The following terms describe concepts within the framework.

Page

A block of html and corresponding client-side programming logic within which application features are presented. All pages occupy the same visual space within an application, and can be switched in and out according to application logic. Typically, pages are sandwiched between a static header and footer. The browser framework has methods to manage pages. All the assets for a page are hot-loaded when first used.

Dialog

Like a page, a dialog is a block of html and JavaScript within which application features are presented. Dialogs are modal bits of user interface that temporarily take over all user interaction, for example to display a message, or prompt the user to login. The browser framework has methods to manage dialogs. All the assets for a dialog are hot-loaded when first used.

Panel

This is similar to a page, in that a region of the screen can be reserved to swap in and out different bits of functionality. However, panels can appear anywhere, typically inside of a page. The browser framework has methods to manage panels. Panels are hot-loaded when first used.

Templates

These are reusable blocks of html, typically used as partials.

Feature

A self-contained set of functionality that is potentially reusable. For example, a common header used between different applications might be written as a feature. Features allow you to create a large set of functionality where all the assets are local to the feature itself. Features are also a useful way to manage a complex application.

Pruning

Pruning is the process of removing a page, panel or dialog from the DOM when it is not in use. However, some pages should not be pruned, and this can be specified when describing the page. This is frequently needed for pages that contain flash objects, since in many browsers, the flash object will reload when added back to the DOM.

Cache Busting

Assuring that changed assets will not be in the user’s browser cache.

Third Party Libraries

MooTools

Mootools is a JavaScript library that works on both the client and server. MooTools is different from a number of libraries, such as jquery and underscore, that namespace all of its APIs, in that it extends native types as well as creating new types of its own.

For example, MooTools has a method to test the presence of an item in an array. So, rather than having something like Mootools.arrayContains(a, v), the method is implemented in the Array prototype, and is directly available on the array itself.

['a', 'b', 'c'].contains('c'); // returns true
['a', 'b', 'c'].contains('d'); // returns false

However, the primary reason for using MooTools is to get access to its class facilities. It can not only create new classes, but it supports a number of other features such inheritance, class reopening, mixins and monkey patching.

Inheritance

To inherit from a class, use the member Extends when creating a new class. This has to appear as the first member in the new class. For example:

var MyClass = new Class({
    Extends : BaseClass
});

To access your parent’s version of a method, call this.parent();

Class Reopen

To reopen a class, use the implement method on the Class to be reopened. For example:

MyClass.implement({
    newMethod : function()
    {
        . . .
    }
});
Mixins

With Mootools you can create classes whose sole purpose is to have its methods merged into another class. In some languages this is known as composition, or mixins. To do this, use the member Implements when defining your class. It should follow Extends if you are also inheriting from a base class. For example:

Package('MahJongg', {
    Service : new Class({
        Extends : Sapphire.Eventer,
        Implements: [Sapphire.Services.AjaxService],

        . . .

    })
});
Monkey Patching

Monkey patching is changing the defined methods of an existing class at runtime. This does not create a new class; it changes an existing class by overriding its existing methods to do something new. To do this, use Class.refactor. For example:

var Cat = new Class({
    energy: 0,
    eat: function(){
        this.energy++;
    }
});
Class.refactor(Cat, {
    eat: function(){
        this.previous(); //energy++!
        alert("this cat has " + this.energy + " energy");
    }
});

Notice that to access the original implementation of a method, use this.previous().

Q

Q is a promises library. It is used in a number of places on both the server and the client side code. The API for this module can be found at https://github.com/kriskowal/q/wiki/API-Reference.

Structure of an Application

There are two ways to refer to the structure of the application. There is how it exists on the server, and the other how it exists once it has been delivered to the browser. On the server is a framework to assemble the various pieces of an application and deliver them to the browser. In the browser is a set of libraries that are used to implement the application from the assembled pieces.

Assembling the application is done using the Application object on the server. This is not to be confused with the Application object running on the client. Use the Application object to specify the various pieces of the application such as the body HTML, templates, pages, dialogs, JavaScript and CSS. Once the specification of the application is complete, the Application object will construct the HTML to be sent to the server.

Philosophy

Separate Layout, Presentation and Code

It is a good idea to separate design and engineering efforts. Engineers should not be creating markup and style sheets, and designers should never have to modify JavaScript. Mixing JavaScript directly with design will make both tasks much harder.

To do this, engineers and designers create a contract about the #ids and sometimes .classes of the DOM nodes the JavaScript will need to manipulate, and the JavaScript engineers need never touch the page templates or the CSS. For instance the following is a test page template.

<div id="test-page">
    <h1>Test Page</h1>
    <div id="test-page-name"></div>
    <img id="test-page-image">
    <div id="test-page-message">Here is a list of stuff</div>
    <div id="test-page-container"></div>
</div>

In this example, the designer and the JavaScript engineer will have agreed to the names of various nodes, for example test-page-name and test-page-image. Presumably when this page is displayed, the JavaScript code will fill these nodes with relevant content.

Often, when some HTML needs to be repeated multiple times for a list of items, the JavaScript code will simply construct the relevant HTML in code and then add them to the DOM for each repeated item. This, however, makes it more difficult for a designer to control the presentation of these items.

Instead, in Sapphire, the designer will create an html block that represents the repeated item, which he is free to create and maintain as part of the designing process, without interaction with the engineer. This is called a template. Add the CSS class template to all templates so that the client framework can manage them.

The engineer and designer can agree on the name of this node, and the engineer can create JavaScript to use the template for each item in the list. As with everything else, nodes within this template will have agreed upon names so that the engineer can update the information for each item. The JavaScript library has functions that facilitate this behavior.

For example, the template node might look like this:

<div id="list-item" class="list-item-template template">
    <img id="list-item-img">
    <div id="list-item-description"></div>
</div>

And when it is time to add items to the list would do the following:

var container = $('#test-page-container');

items.each(function (item)
{
    var template =Sapphire.templates.get('list-item');

    template.find('#list-item-img').attr('src', item.url);
    template.find('#list-item-descrption').html(item.description);

    container.append(template);
}, this);

Model/View/Controller

The model/view/controller (MVC) paradigm works well with single page applications, and is recommended. In traditional web applications, the MVC lives on the server, and is responsible for routing control from page to page based on user actions. In a single page application all the application logic lives on the client, so the MVC is moved there. The server code becomes much simpler.

A model is responsible for saving and retrieving a type of data, a controller manages the application flow, and a view updates and monitors the user interface. A view has knowledge of the HTML layout, a model understands the server-side interface and a controller knows about both a model and a view.

Communication from a view to a controller and from a model to a controller should be done via events and callback functions, not via direct calls into a controller.