Skip to main content

Client side MVC: Using Publisher - Subscriber event model to build decoupled components

I've been really busy these days writing client side MVC code. A few months back I explored using Require and Backbone to build the frontend MVC structure. Since then I've been working on improving the structure of my client side code. One approach that I've used is to decouple the components. When I say decouple, the components that I build should not directly alter or affect the behavior of some other component in the page. By decoupling the components, we can ensure that these set of components can be reused and tested in isolation.

Usually it gets way to easy to get digressed while developing a website without following any code structure. You would end up writing tons of callback functions and it would become hard to maintain that code. Applying MVC structure to your client side code would ensure that the concerns are separated and if there is a bug in one of the components it would be easy to find and fix it. As mentioned in my earlier post, Backbone.js provides set of components that can be extended to build Models, Collections and Views. A Model or Collection can be considered as a Data Access Layer, which communicates with your server and maintains the data structure. The responsibility of the View would be to listen to the changes in the Model\Collection and render the page accordingly. On a page you would find components like the header, navbar, footer and various other pieces that make up the body of the page. Each of these components on the page can be considered as widgets on the page. This is how each widget would be structured:

widgets
    - widget_name
         - build
         - collections
         - models
         - templates
         - views

The widgets directory would contain definitions for various widgets. Each of these widgets contain the following directories:

  • Models - a set of Backbone models, here the data structure in each of the model definition is of a simple object containing few keys.
  • Collections - a set of Backbone collections. Collections would contain an array of objects.
  • Views - a set of Backbone views. These views would have a dependency on the collections, models and templates. It's sole responsibility is to listen to the changes in the Collections or Models and render the page accordingly.
  • Templates - a set client side templates. I have used Underscore.js templates since Backbone already has a dependency on Underscore.
  • Build - the build directory would contain the optimized code (created using Require.js optimizer - r.js).
Once you build these widgets, there would be many instances where the state of one widget would alter the state of another widget. For example, consider that you are building a 'Cart' and a 'Wishlist' widget. In a scenario where you want move an item from Cart to Wishlist or vice versa, these entities should have each other's reference to complete this action. Instead of these widgets storing a reference of another widget on the page, a central entity - 'Controller' would assist in sending messages between these widgets. The Controller would provide a Publisher - Subscriber event model, using which these widgets would publish and subscribe to events:


The Controller uses a 'events' object to store the list of events and its subscribers and it provides methods - 'subscribe' and 'publish' as an interface to other widgets:


The above code snippet defines a module which contains definitions for 'subscribe' and 'publish' methods. The widgets declare a dependency on the 'Controller' and would publish events by calling the publish method:

Controller.publish('movetowishlist', cartObject);

and subscribe to events using

Controller.subscribe(view, 'movetowishlist');

The 'subscribe' method first checks whether the specified 'eventname' already exist. If not, then it creates an entry in the events object. The value of this object is an array of subscribers. Here you also check whether the event was already published. If it has been published then the subscriber would get the published data as soon as it subscribes for the event. This is required when you have multiple widgets on the same page and these widgets have subscribed to the 'loadComplete' event of other widgets on the page. These widgets are loaded asynchronously and when the second widget loads it would not receive the 'loadComplete' event of the first widget. Therefore, we store the data published by widgets and provide it other widgets when they subscribe to it.

The subscriber's view object needs to implement a custom event (in this case 'movetowishlist'), so that when the event is published, the Controller would invoke this event on the subscriber's object. The publish method checks if the specified event already exists in the 'events' object and then iterates over the list of subscribers and publishes the event on the subscribers object. It then stores the published data in the 'publishedEvents' object, this is required for 'loadComplete' events.

The publisher - subscriber event model helps in decoupling the widgets on the page, allowing you to reuse them wherever required. The Controller here acts as a mediator between different widgets on the page and provides a communication channel for these widgets to interact with each other.

Comments

Popular posts from this blog

How to use the APP_INITIALIZER token to hook into the Angular bootstrap process

I've been building applications using Angular as a framework of choice for more than a year and this post is not about another React vs Angular or the quirks of each framework. Honestly, I like Angular and every day I discover something new which makes development easier and makes me look like a guy who built something very complex in a matter of hours which would've taken a long time to put the correct architecture in place if I had chosen a different framework. The first thing that I learned in Angular is the use of the APP_INITIALIZER token.

De-obfuscating javascript code in Chrome Developer Tools

I had blogged about JavaScript debugging with Chrome Developer Tools  some time back, wherein I have explained how these developer tools can help in debugging javascript code. Today Google Chrome 12 was released and my Chrome browser was updated to this version. As with every release, there have been some improvements made on performance, usability etc,. One feature that stood out for me is the ability to De-obfuscate the javascript code. What is Minification? Minification is the process of removing unnecessary characters such as white spaces, comments, new lines from the source code. These otherwise would be added to make the code more readable. Minifying the source code helps in reducing the file size and thereby reducing the time taken to download the file. This is the reason why most of the popular javascript libraries such as jQuery are minified. A minified jQuery file is of 31 KB in size where as an uncompressed one is about 229 KB. Unfortunately, debugging minified javascript f...

Using MobX to manage application state in a React application

I have been writing applications using React and Redux for quite some time now and thought of trying other state management solutions out there. It's not that I have faced any issues with Redux; however, I wanted to explore other approaches to state management. I recently came across MobX  and thought of giving it a try. The library uses the premise of  `Observables` to tie the application state with the view layer (React). It's also an implementation of the Flux pattern wherein it uses multiple stores to save the application state; each store referring to a particular entity. Redux, on the other hand, uses a single store with top-level state variables referring to various entities.