Ajax MVC - RIA Design Pattern

Model-View-Controller (MVC) Design Pattern is a way to structure software. As web applications are moving towards Rich Internet Applications (RIA), the client side is growing more complex. Complex systems benefit the most from adapting architecture and design patterns. MVC is a platform-independent pattern that can be used with any RIA framework — or even without one. I'll show in this tutorial how it can be used to structure an Ajax-application, using Dojo Toolkit in my samples.

I did a presentation about this topic at WebExpo 2009 in Prague. You can check out the slides below (embedded from Google docs) and then continue reading my selected notes and some advanced code samples.

Complexity in Rich Internet Applications

Rich Internet Applications are called rich because because they provide features that are not natively supported by browsers. The idea is to make them highly interactive and to bring user experience closer to that of desktop applications. These RIAs are typically built with both plugin-based frameworks such as Flash/Flex and Silverlight, and web standards-based Ajax-frameworks such as jQuery and Dojo Toolkit. These Ajax-based frameworks are stretching the capabilities of the technologies supported by browsers, namely HTML, CSS and JavaScript. RIA environment introduces a certain amount of complexity that's not such a concern in traditional web applications, including online/offline state and accessibility. Additionally, web developers becoming increasingly software designers means that a whole bunch of people have to learn new techniques to master this complexity.

Due to this increasing complexity, web developers have to adapt architectures and design patterns that have been already tested and validated in other environments. MVC is one such a pattern. Even though its origins are in Graphic User Interface (GUI) applications, many web developers know it from server side by platforms like Django, CakePHP, Struts and ASP.NET MVC. Thanks to new development in RIAs, it's again becoming an interesting topic on the GUI-side. A recent article JavaScript MVC and the conversation that it sparked illustrate this.

Problems of synchronization

The complexity of RIA is not solely due to the novelty but it's inheritent to any networked applications. As Martin Fowler explains there are three copies of data involved:

  1. Record state – in the database. Shared by multiple clients
  2. Session state – in the memory within application. Maybe shared by several UI components.
  3. Screen state – inside UI components, what the user sees.

Keeping all them synchronized is a challenge. Data Binding is a technique that has been developed to ease this, by observing changes in one copy of data and propagating them to the next one. It's closely related to MVC pattern and will be discussed later in the article.

Benefits of MVC

Is all about Separation of Concerns, in the same way as HTML and CSS separate content from style. The purpose of this separation is to enable the programmer to focus his attention to only one aspect of the system, and ignore the other parts which are irrelevant for the current issue.

MVC might be critisized for slowing down development, making the application larger or being unsuitable for web development. This kind strict layers might make initial development slower, but just like commenting code, it enables you to maintain the code. Size and speed have been primary concerns in web development, but this different is minimized as production code is typically compressed, and additionally advanced javascript processing in new browsers provides lightning fast execution. It might be a wrong method for some web applications, but since maintenance is not that much of an issue for smaller projects, this design pattern benefits more larger projects.

Ajax MVC

The three components of MVC can be characterized as:

  • Model: Data, knowledge, defines business logic, i.e. rules.
  • View: Presentation of model data.
  • Controller: Handles user interaction.

View and Controller are closely connected. Together they form UI component (a.k.a widget) that contains both its presentation and control structure. How are these three components connected together? Have a look at the slide 8 for two flow diagrams to get idea of two different implementations. What I am describing here close to JavaScript MVC / Apple Cocoa idea of MVC. But I feel that Mendelt puts it the best:

Discussions about what 'real MVC' is are usually not really constructive anyway. MVC is a pattern developed for smaltalk about 30 years ago. None of the implementations that are used now are exactly the same anymore, many implementations are improvements. You've got different flavours called MVP, MVVM, Document-View etc. And they are all usefull in some cases. The important thing to take away from this is that its a good idea to separate your UI logic from your application logic.

I'll go through now how to present MVC components in the world of Ajax. Code samples over here are based on Memory Game web application I created for learning purposes. Go ahead, check it out and improve your memory by matching Flickr pictures. And return back here to understand how it works.

Model

In the Memory Game, Model actually consists of three layers: Flickr provides database-like server side component via its open APIs, Dojo's FlickrRestStore is a data access object that provides a standard interface to Flickr APIs, and finally custom object Deck encapsulates business logic i.e. game rules.

Flickr APIs are nicely wrapped by dojox.data.FlickrRestStore into a standard Dojo Data interface. Images can be queried easily in the following fashion:

dojo.require("dojox.data.FlickrRestStore");

dojo.addOnLoad( function() {
	var store = new dojox.data.FlickrRestStore();
	var request = {
		apikey: "8c6803164dbc395fb7131c9d54843627",
		text: "lol catz"
	};
	
	store.fetch({
		count: 10,
		query: request,
		onComplete: function( data, query ) { console.log( data, query ); },
		onError: function( m ) { console.error( 'error', m ); }
	});
} );

Go ahead — try it in FireBug console on any page that includes dojo!

Custom Deck object extend JavaScript native Array by adding few methods that implement a subset of Dojo Data Write and Notification APIs in order to make the model observable, meaning that submits a signal every time data has been changed. See Data Binding underneath for further discussion. Here it's enough if I demonstrate setValue and onSet functions:

/**
 * Set the value of an property of an item
 * 
 * Unlike in ItemFileWriteStore, we don't do any checks (item is a member
 * of array) over here, but instead leave it up to the user to be sensible. 
 */
setValue: function( /* item */ item, 
       /* string */ attribute,
       /* almost anything */ value) {
	var oldValue = item[attribute];
	item[attribute] = value;
	this.onSet(item, attribute, oldValue, value);
},

/**
 * This function is called any time an item is modified via setValue
 * 
 * Its purpose is to provide a hook point for monitoring actions
 */
onSet: function(/* item */ item, 
       /* attribute-name-string */ attribute, 
       /* object | array */ oldValue,
       /* object | array */ newValue) {
	
}

The Deck-array holds a set of cards that are simple JavaScript objects. Instead of changing card property value directly in the standard JavaScript way item.attribute = newValue, you'll do it via Deck's setValue deck.setValue(item, attribute, newValue). This way these set operations always generate an onSet event into we can easily attach to from our controller. Additionally, Deck fires two other events onLoad and onGameOver when business rules so dictate. Again they can be listened to by the controller. Thanks to this kind of structure the model knows nothing of the controller, instead it's only generating events that can be handled by any module.

View

In this MVC implementation the View consist only of HTML, i.e. DOM. It might be good to move some operations from controller to view, such as the animations shown when opening a card. However, in this case I don't think this is a strong enough reason to build yet another layer. Instead, I simply treat View as HTML/DOM, enchanced by templating. There are several options for building a view:

  1. Use existing HTML
  2. Request/create a new block of HTML
  3. Create HTML using DOM

Templating is a way of creating a block of HTML without glueing tags and small pieces of data together. So, instead of: var html = '<h2>' + data.name + '</h2>'; you could do something like this using Dojo's implementation of Django Templating Language (DTL):

<table>
  <tbody>
    {% for row in rows %}
    <tr>
      {% for cell in row %}
        <td>
          <div dojoAttachEvent="onclick: onCardClick" cid="{{ deck.current.id }}" class="card">
            <img src="{{ deck.next.url }}" />
          </div>
        </td>
      {% endfor %}
    </tr>
    {% endfor %}
  </tbody>
</table>

Two things you might find unordinary here are custom attributes dojoAttachEvent and cid. The first one indicates what events should be listened to; the second one gives us a way to identify unique cards/nodes.

Controller

The controller listens to events triggered by both View (user action) and Model. Dojo provides a perfect mechanism for this, since it doesn't separate between DOM events and "JavaScript events" i.e. function calls. Remember how model set operations always generate an onSet event? We can easily attach to this event from our controller: dojo.connect( this.deck, 'onSet', this, 'deckEvent' ); in order to update UI accordingly. How does the event flow work in this MVC setup? Let's take flipping a card as an example:

  1. User clicks on a card
  2. This generates onCardClick event
  3. Controller catches this event and delegates to the model
  4. Model checks that the card is not open (business logic)
  5. Model updates itself and sets the card open
  6. A change in model generates onSet event
  7. Controller catches this event and updates the UI

If we have a closer look at the last step:

postCreate: function() {
	// ...	
	// onSet events reflect cards opening and being (not) matched 
	dojo.connect( this.deck, 'onSet', this, 'deckEvent' );
},

deckEvent: function( item, attribute, oldValue, value ) {
	console.log( '[mg.Board] Registered store update on: ', arguments );
	
	var node = dojo.query('*[cid='+item.id+']', this.domNode)[0];
	
	if( oldValue == mg.Card.states.HIDDEN &&
			value == mg.Card.states.OPEN ) {
		this.flipCardOpen( node );
	} else if( oldValue == mg.Card.states.OPEN &&
			value == mg.Card.states.HIDDEN ) {
		this.pairNotMatching( node );
	} else if( oldValue == mg.Card.states.OPEN &&
			value == mg.Card.states.PAIRED ) {
		this.pairMatching( node );
	}
},

In the "constructor" postCreate we set the controller to listen to the onSet event in Model. Once it happens, we check what actually happened and update the UI accordingly. The part of the UI that should be updated is accessed via DOM node's custom cid attribute that holds the cards Id.

Data Binding

Data binding is an answer to the synchorinization problem introduced above. The idea is that changes in view or model are automatically propagated from one to another. In Ajax world there are no automatic tools for this, plus additionally the lack of standard JavaScript setters and getters makes things more complicated. Instead of being able to connect observers into getters and setters, you need to perform two actions to update both model and view:

this.title = newValue
document.getElementById('title').innerHTML = newValue

Again Dojo provides some helpful tools: Dojo widgets have attr method that lets you tie widget properties to widget layout DOM nodes. Thanks to this mechanism one command is enough to update both. It requires that you specify these connections in attributeMap property of your widget.

widget.attr('foo', 'assigned a new value'); 

Have a look at this minimal sample I created: Demonstrating Dojo widget (Dijit) Data Binding. It's in the JS Bin, so you can easily play with it. Start by executing the sample command from FireBug console and then continue modifying the script to see your changes instantly.

For those of you who feel adventurous, there's hack in dojox.lang.observable that lets you use "native" getters and setters (instead of attr function) in cross-browser way. It's based on the way how IE allows you define getters/setters in VBScript and then access object created this way from JavaScript. You can read about my experiments at Widget template bindings with 'native' getters/setters, Observable and Widget.attr().

References

I found the following works valuable when working on this article:

  • GUI Architectures by Martin Fowler — about MVC and Data Binding in general, plus MVC's origins in SmallTalk GUI.
  • MVC in the Browser by Kris Zyp — how Dojo is build MVC on mind, even though not explicitly spelled out.
  • JavaScript MVC by Jonathan Snook — minimal MVC approach that started a long conversation at A List Apart.
  • The Model-View-Controller Design Pattern by Colin Moock — about applying MVC on Flash app design.
  • Ajax in Action by Dave Crane and Eric Pascarello with Darren James — right after the term Ajax was coined these guys described how to use MVC to structure RIA. Manning Publications offers the most interesting chapter The page as an application for free download.