How to Refactor a Mess into an Organized Web Application (Part 1: the BPC Architecture)
This page has been translated into Spanish language by Maria Ramos. »» Cómo Refactorizar un enredo en una Aplicación Web Organizada (Parte 1: la Arquitectura BPC).
In the former post I said that we would be refactoring chaos into order.
This article is the next in the series; and here we will be laying the foundations of an organized web application.
Outline
Here is an outline of what we will do in this article:
- We will start with a working application where everything is dumped in a single page;
- Then we will move external assets (such as CSS and JS files) to proper folders;
- Then we will enclose JavaScript functionality into a module;
- And finally we will create a basic folder hierarchy for our application.
Ready? Let’s give it a go
.
Initial Application
Let us start with a primitive application. The application contains a single button.
Clicking this button displays a simple user profile card. Here’s what the application looks like:
Nothing fancy. The app consists of a single button. Clicking on that button hides the button and displays some profile information.
And here is the code to make it happen:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>VCard Demo</title>
<script language="javascript">
function showVCard() {
document.getElementById('VCardActivator').style.display = 'none';
document.getElementById('VCardContent').style.display = 'block';
document.getElementById('VCardContent').innerHTML = ' \
<h1>Volkan Özçelik</h1>\
<dl>\
<dt>Web</dt>\
<dd><a href="http://o2js.com">o2js.com</a></dd>\
<dt>Email</dt>\
<dd><a href="mailto:volkan@o2js.com">volkan@o2js.com</a></dd>\
<dt>twitter</dt>\
<dd><a href="http://twitter.com/linkibol">@linkibol</a></dd>\
<dt>LinkedIn</dt>\
<dd><a href="http://linkedin.com/in/volkanozcelik"\
>linkedin.com/in/volkanozcelik</a></dd>\
</dl>\
<p class="clear"><a href="javascript:void(closeVCard())" class="close" title="close"\
><span>back to home</span></a></p>';
}
function closeVCard() {
document.getElementById('VCardActivator').style.display = 'block';
document.getElementById('VCardContent').style.display = 'none';
}
</script>
<style type="text/css">
// some CSS goes here.
</style>
</head>
<body>
<div id="VCardContainer">
<p id="VCardActivator"><a href="javascript:void(showVCard())"
class="super button action" id="vcard-volkan">Volkan Özçelik</a></p>
<p id="VCardContent"></p>
</div>
</body>
</html>
You can reach this version of the code at this github history entry.
It Works; but it “Smells”
The application seems to be doing what it’s supposed to do. So what’s wrong with it?
Let’s list a few:
- There’s no DOCTYPE.
- The CSS is inline. Moving it to an external css file, will help browsers cache it and this will help decrease the application’s load time.
- Moreover, decoupling CSS, JavaScript and HTML is a widely-accepted best practice. See o2.js JavaScript Conventions and Best Practices for a detailed review of why it is so.
- The JavaScript is on the top, we need to move it to the bottom of the page for a better perceived page speed.
- The attribute language, in script tag is deprecated.
- All the images are in the root folder. It will be better if we move them to an images folder of their own.
Move Assets to External Folders
First, let’s remove the images to the /images folder. You can see the new structure at this github history entry.
Now let’s add a doctype, move CSS and JavaScript to external files, and move scripts to bottom. Here’s how the code looks like after doing all these:
<!DOCTYPE html>
<html>
<head>
<!--
This program is distributed under
the terms of the MIT license.
Please see the LICENSE file for details.
-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>VCard Demo</title>
<link rel="Stylesheet" type="text/css" href="/o2.js/examples/vcardapp/css/master.css" />
</head>
<body>
<div id="VCardContainer">
<p id="VCardActivator"><a href="javascript:void(showVCard())"
class="super button action" id="vcard-volkan">Volkan Özçelik</a></p>
<p id="VCardContent"></p>
</div>
<script type="text/javascript" charset="utf-8" src="/o2.js/examples/vcardapp/js/index.js"></script>
</body>
</html>
You can view this version of the code at this github tree.
Make Your JavaScript “Unobtrusive”
The purpose of markup is to describe a document’s structure, not its programmatic behavior. Combining the two negatively impacts a site’s maintainability for similar reasons that combining content and presentation does.
More “organized”, huh? And still there are things missing:
- Take href=”javascript:void(showVCard())” for example —
javascript: is a pseudo-protocol, which means that our application will not work if JavaScript is not supported. - This also means that our profile pages will not be crawled by search engines.
- In addition to all these, we are binding inline JavaScript to HTML links, which is strictly against our “decouple HTML and JavaScript” principle.
- Moreover, inline event handlers are harder to use and maintain:
- You cannot set handlers for several events on a single element,
- And it’s harder to set the same event handler on several elements,
- Worst of all, inline event handlers mess up with event delegation implementations.
Most programming languages contain good parts and bad parts. I discovered that I could be a better programmer by using only the good parts and avoiding the bad parts. After all, how can you build something good out of bad parts? — Douglas Crockford
One can argue that if designed carefully, inlining JavaScript would not cause problems. But instead of “cautiously” controlling our code-base at all times (which we won’t
be doing anyway), wouldn’t it be a saner approach to choose an architecture that enables our program grow large without collapsing into a puddle of confusion?
If that makes sense, let’s get rid of the inline scripts in the code:
<p id="VCardActivator"> <a href="/" class="super button action" id="vcard-volkan">Volkan Özçelik</a> </p>
And the relevant script in the /js/index.js:
document.getElementById('vcard-volkan').onclick = function() {
showVCard();
document.getElementById('vcard-volkan-close').onclick = function() {
closeVCard();
return false;
}
return false;
}
For the interested: We will handle the graceful degradation of the application (for non-JavaScript-supporting user agents) in the next parts of the series.
You can reach this version of the code at this github snapshot.
onclick?! Come On!
I know that we are not in the year 2000, DOM Level 1 event registration is outdated, and there are better ways to do event binding than using onclick.
In deed the code is just in a transitional phase. We will be working more on the application’s event handling in upcoming articles — just bear with me.
Don’t be deceived by the simplicity of this application. I intentionally try to keep it simple to better demonstrate the core concepts of layering and refactoring.
In a real world app, you will be most possibly working with production code that millions of people are using daily. In such cases, it will be a good practice have a kaizen-like mindset and achieve an efficient, organized and optimized code via incremental changes.
Whether you have a few hundred lines of JavaScript code, or a few hundreds of thousands does not matter; you can still apply the principles you’ll see in this series of articles to your code.
Dare to walk with me with tiny little steps (while making sure that you don’t break anything at each incremental step) towards a better-organized code?
This is also a refactoring best practice:
- Do small changes,
- Test those changes,
- Make sure they work,
- Rinse and repeat.
Remember, you are not revolutionizing your app; rather you are evolutionizing your app.
And evolution is an incremental, slow-paced and intelligently-designed process (pun intended).
Keep Your Global Namespace Clean
One more thing, we are currently polluting the global namespace. In index.js, we have several global functions neither of which needs to be so.
We need to encapsulate global functionality inside modules.
/*
* <!--
* This program is distributed under
* the terms of the MIT license.
* Please see the LICENSE file for details.
* -->
*/
(function(document){
'use strict';
function showVCard() {
document.getElementById('VCardActivator').style.display = 'none';
document.getElementById('VCardContent').style.display = 'block';
document.getElementById('VCardContent').innerHTML =
['<h1>Volkan Özçelik</h1>',
'<dl>',
'<dt>Web</dt>',
'<dd><a href="http://o2js.com">o2js.com</a></dd>',
'<dt>Email</dt>',
'<dd><a href="mailto:volkan@o2js.com">volkan@o2js.com</a></dd>',
'<dt>twitter</dt>',
'<dd><a href="http://twitter.com/linkibol">@linkibol</a></dd>',
'<dt>LinkedIn</dt>',
'<dd><a href="http://linkedin.com/in/volkanozcelik"',
'>linkedin.com/in/volkanozcelik</a></dd>',
'</dl>',
'<p class="clear"><a href="/" id="vcard-volkan-close" class="close"',
' title="close"><span>back to home</span></a></p>'].join('');
}
function closeVCard() {
document.getElementById('VCardActivator').style.display = 'block';
document.getElementById('VCardContent').style.display = 'none';
}
document.getElementById('vcard-volkan').onclick = function() {
showVCard();
document.getElementById('vcard-volkan-close').onclick = function() {
closeVCard();
return false;
};
return false;
};
}(this.document));
That’s better
.
You can view this version of the code at this github history snapshot.
Factor out Common Constants
Now, let us factor out common strings and constants.
This will help us move them to appropriate files in the following tutorials.
/*
* <!--
* This program is distributed under
* the terms of the MIT license.
* Please see the LICENSE file for details.
* -->
*/
(function(document){
'use strict';
/*
* Common constants.
*/
var kNone = 'none';
var kBlock = 'block';
/*
* Common elements.
*/
var kActivatorDiv = 'VCardActivator';
var kContentDiv = 'VCardContent';
var kVCardButton = 'vcard-volkan';
var kVCardCloseButton = 'vcard-volkan-close';
/*
* Common HTML
*/
var vCardHtml = [
'<h1>Volkan Özçelik</h1>',
'<dl>',
'<dt>Web</dt>',
'<dd><a href="http://o2js.com">o2js.com</a></dd>',
'<dt>Email</dt>',
'<dd><a href="mailto:volkan@o2js.com">volkan@o2js.com</a></dd>',
'<dt>twitter</dt>',
'<dd><a href="http://twitter.com/linkibol">@linkibol</a></dd>',
'<dt>LinkedIn</dt>',
'<dd><a href="http://linkedin.com/in/volkanozcelik"',
'>linkedin.com/in/volkanozcelik</a></dd>',
'</dl>',
'<p class="clear"><a href="/" id="vcard-volkan-close" class="close"',
' title="close"><span>back to home</span></a></p>'
].join('');
function showVCard() {
document.getElementById(kActivatorDiv).style.display = kNone;
document.getElementById(kContentDiv).innerHTML = vCardHtml;
document.getElementById(kContentDiv).style.display = kBlock;
}
function closeVCard() {
document.getElementById(kActivatorDiv).style.display = kBlock;
document.getElementById(kContentDiv).style.display = kNone;
}
document.getElementById(kVCardButton).onclick = function() {
showVCard();
document.getElementById(kVCardCloseButton).onclick = function() {
closeVCard();
return false;
};
return false;
};
}(this.document));
Have you noticed a minor change in the showVCard function?
function showVCard() {
document.getElementById(kContentDiv).innerHTML = vCardHtml;
document.getElementById(kContentDiv).style.display = kBlock;
}
Organize Your Folder Structure
Now let’s add some folders to our project.
These folders will create a skeleton for the upcoming tutorials:
As their names imply, css and images folders are for holding static css and image assets.
Now let’s briefly go over the remaining folders in this structure:
include
The include files and partial pages that PHP uses will be in this folder.
include/business
This folder is represents the “Business Tier”.
The “Business Tier” includes “business objects”.
Therefore all “business objects” go into this folder.
These “business objects” read data from the “Persistence Tier” to retrieve application’s state information, and write data to the “Persistence Tier” to update the application’s state information.
include/common
These are the common includes that all pages of the application share.
include/config
Application configuration and globally-used constants go here.
include/init
Pages’ initialization logics go here.
include/presentation
That’s the “View” part of our application.
Things like header, footer, navigation, custom reusable user interface controls and snippets will be here.
include/persistence
This folder demonstrates the server-side “Persistence Tier”.
It can be the file system, or a relational database, or some cloud storage, or a memory cache of some sort. What “Persistence Tier” does is to abstract all those implementation details and provide a unified interface.
The “Business Tier” reads data from the “Persistence Tier”, and executes business functions with them.
The “Business Tier” can also write data to the “Persistence Tier”.
service
The “Service Tier” demonstrates an external API.
This can be an API that our web application itself provides, or some external API that our web application consumes.
js/behavior
The “Behavior Tier” is the heart and brain of the client-side application.
In MVC terms, you can think of it as a business-logic-aware Controller.
It is responsible for:
- Updating the “Presentation Tier” with the new data,
- Responding user-event-driven requests from the “Delegation Tier”.
The “Behavior Tier” reads data from and writes data to the client-side “Persistence Tier”.
Please note that client-side persistence is a totally different concept than the server-side “Persistence Tier” mentioned above. As its name implies, the client-side “Persistence Tier” is responsible for persisting state data on the client side. This can be done using cookies, flash local object, HTML5 local storage, HTML5 WebSQL Database, window.name attribute as a cache, and even global static shared JavaScript objects.
The “Behavior Tier” can call the methods of the “Communication Tier”.
The “Behavior Tier” does not directly receive method calls from the “Communication Tier”; instead, the “Communication Tier” chains this responsibility to “Delegation Tier” via callbacks. That is to say, the “Delegation Tier”, notifies the “Behavior Tier” when an event delegate is triggered.
The “Behavior Tier” can also call the methods of the “Presentation Tier”.
The “Behavior Tier” is where various event handler delegates (of the “Delegation Tier”) are bound. Moreover, the “Behavior Tier” can even directly trigger a delegate of the “Delegation Tier”, if necessary.
In short, the “Behavior Tier” has great power and control over the application. And with great power, comes great responsibility. This tier has to be very carefully crafted
.
js/bootstrap
This is the main bootstrapper.
The client-side application’s life cycle starts here.
The bootstrapper registers events and lazy-loads required modules.
js/communication
The “Communication Tier” is where the client sends messages to, and responses from
external APIs are received from. These requests can be an AJAX, JSONP, or CORS request; a websocket connection, an iframe proxy, a flash proxy, an ActiveX component, or even an HTC component…
The main responsibility of the “Communication Tier” is to decouple the prox(ies) used to communicate with the outside world, so that they can be easily replaced with alternative implementations without having a major effect on the remaining parts of the code.
The “Communication Tier” can send messages to (as callbacks) the “Delegation Tier” and receive messages from (as API requests) the “Behavior Tier”.
The “Communication Tier” cannot talk directly to the “Presentation Tier”; it delegates presentational changes to the “Behavior Tier” via “Delegation Tier” callbacks, instead.
The “Communication Tier” cannot directly call the “Behavior Tier” as well.
In short, all the signaling of the “Communication Tier” and other tiers, are proxied over the “Delegation Tier”.
The “Communication Tier” can read data from the client-side “Persistence Tier”, but it cannot write data to it.
js/config
Application configuration data and common constants will be here.
js/delegation
The “Delegation Tier” is the layer between the application logic and the user interface.
When a user interaction (such as a click event) occurs, the “Delegation Tier” handles it and forwards it to a proper Controller on the “Behavior Tier”, which then decides what to do.
The “Delegation Tier” cannot directly call the “Presentation Tier” or the “Communication Tier”. It chains this responsibility to the “Behavior Tier”.
The “Delegation Tier” can read data from the client-side “Persistence Tier”, but cannot write to it.
js/lib
External libraries will go here.
js/presentation
The “Presentation Tier” is where the DOM rendering takes place. Any rendering, repainting, reflowing, notification, prompt, alert… happens in this tier.
The “Presentation Tier” methods are only called from the “Behavior Tier”.
The “Presentation Tier” cannot call the “Communication Tier” directly, and vice versa.
The “Presentation Tier” can read data from the client-side “Persistence Tier”, but it cannot write data to it.
Meet the BPC Architecture
I hope the diagram below will put everything written above in perspective:
Although the relationship between the above tiers pretty much follows the MVC Philosophy, I don’t want to call it an MVC application. Here’s my reasoning:
In a nutshell a true MVC architecture works like this:
- There is a model that is at the heart of the whole thing.
- If the model changes, it notifies its observers that a change has occurred.
- The view is the stuff you can see and the view observes the model.
- So, as soon as the model changes; the view is notified that the model has changed, and the view changes its appearance accordingly.
- The client can interact with the view (e.g. clicking, dragging etc.) but the view is dummy and does not know what to do.
- So the view tells the controller what the user did (via event handler callbacks) and assumes the controller knows what to do.
- The controller appropriately changes the model.
- Then the model notifies the view, and it goes on and on like this.
And (as always) there is a catch:
The client-server environment of web-applications makes things more complicated than the original environment that MVC was invented in (i.e. smalltalk). This is mostly because the view can’t easily observe the model (at least not without some kind of Comet communication).
Therefore in almost all of the so called “Web-Based MVC Applications”, the Controller is just a middle-man implementing either the “mediator“, or the “strategy“ pattern. (And both of these patterns deserve articles on their own — we will dive them deep in upcoming tutorials.)
In short, in web applications the Controller simply collects all the needed data from the model and passes it to the View. Given that the application operates in a client-server environment the View does not update automatically when the Model changes.
Unlike traditional MVC, where the Model is the heart; in Web-Application MVC, the Controller carries on the big responsibility. It both updates the Views, and changes the Model. Moreover, strictly speaking, the View is not an Observer: Since there is not a true pub-sub mechanism between them, the Model does not notify the View.
Thus I don’t think MVC is a “best fit” for web applications either.
Call me picky, but for all the above reasons I don’t want to address the concept of separating presentation, data, and functionality and putting a mediator in between them a “MVC Framework”.
I would rather call this kind of architecture a “Behavior-Presentation-Communication” (BPC) Application. And I’m open to your insights and recommendations on this
.
Conclusion
This completes our first part in refactoring a chaos into an unobtrusive, accessible, modular, organized, and layered web application. You may also be interested in Part Zero (how it all began).
Next up? We will be moving all the methods in the index.js to their appropriate tiers. We will also split index.php to several server-side includes, add some AJAX to spice things up; and will make sure that our application is equally usable while JavaScript is disabled.
Stay tuned for the next article
.
And until then, feel free to add your comments and suggestions
.





I am totally in favor of using a client side MVC framework to make more modular and separated into responsibility driven layers. I have worked on few js frameworks lke backbone , JavascriptMVC and i totally love the fact that model notifies view and views update themselves. The pub sub pattern implemented by these framework and works seamlessly. I think now a days ember, spine, knockout and other frameworks are the talk of the town and can be find in most of the webapps.
What your take on this?
It’s really hard to summarize things in a comment, and I believe this deserves a separate blog article in an of itself. Let me try to elaborate on this anyway:
I personally like Backbone’s View-Centric approach. In a web application Views should be intelligent; once you update a model, the view should be notified via events and it should be up to them to when and how to render themselves.
From the broadest perspective we can think of the model as the database, the view as the user’s browser, and the controller as the actual ui elements on the browser (like the back button, refresh button, the scrollbar and such)
And I too am frustrated about the (mis)interpretation of MVC in its current state. Backbone, Spine, Ember, JS MVC… and the like are great frameworks of different flavors that enable their users to rapidly create great products. And all of these frameworks are based on the concept of separating design from functionality; and they have different takes on this. And I still do believe that none of these frameworks are MVC frameworks.
If I re-quote myself:
“Call me picky, but for all the above reasons I don’t want to address the concept of separating presentation, data, and functionality and putting a mediator in between them a “MVC Framework” (ref: http://o2js.com/2011/12/10/introduction-to-the-bpc-architecture/)
Yet, I am not going to “set the record straight” or try to offer a single definitive answer to this. There are a lot of different flavors of MV-whatever, and none of them are not and cannot be strictly MVC (in terms of Smalltalk MVC sense) anyway. Over the course of years where models, views, and controllers live have shifted from being strictly on the server to being strictly on the client with a loose proxy to the server to replicate/sync the server data.
So as the paradigms shift from a good old non-javascript client-server MVC to a more client-centric MVC; it’s logical for the view to gain more emphasis and more responsibility.
Hope that could shed some light on your question.