Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Challenges and solutions - Architecture of a Modern Web Application - Part 1

0.00/5 (No votes)
2 Oct 2013 2  
Challenges and solutions to architect device independent maintainable web applications using latest web technologies.

Article Series

Introduction

This article is about popular challenges in web application development and their solutions.

Modern web applications could easily be decomposed into two distinct code bases. One that runs on server infrastructure and the other that runs on end user devices. The server infrastructure could be as big as over 49,293 servers in just one Google data center and could be as small as a website hosted on a company's internal web server.

Figure - Google Data Center

On the other hand, end users use web applications on myriad of devices, ranging from desktops, laptops, tablets, HDTV, printers, phone display and smart phones. All these devices come in variety of screen sizes, native frameworks and browser compatibilities for CSS3, JavaScript, and HTML5.

This leaves web application development teams in making variety of decisions regarding application compatibility, performance, usability and maintainability. Let's discuss device and server side code bases separately.

Figure - One way to visualize web application

Device Codebase

Popular end user devices like laptops, tablets and smart phones could run two different kind of web applications.

  1. A responsive HTML website like Boston Globe which runs on all kind of devices equally well, or
  2. A native web application like Evernote which runs on popular smart phones like iPhone 5, Glaxay S2, Nokia Lumia, or BlackBerry Q10.

A responsive web application developed using HTML5, CSS3, and JavaScript could be viewed on phone browser while native web application runs directly from device. Native apps are built using:

  1. Device SDK, e.g., iOS SDK, Android SDK, Windows Phone SDK or Black Berry SDK, or
  2. Cross platform SDKs PhoneGap, Flex, or Xamarin, or
  3. HTML 5 and JavaScript based hybrid frameworks like Kendoui Mobile, Sencha Touch‎, or jQuery Mobile.

Figure - Native application developed using KendoUI Mobile and PhoneGap

Let's discuss responsive and native web applications in a bit more detail.

A. Responsive Web Application

Some web applications render on smart phones, HDTV & tablets while others do not. A public website like Netflix is viewed on different device sizes from laptops to HDTV and smart phones. To support a variety of device sizes while keeping a single code base, a website should use responsive web development technologies like CSS3 or frameworks like Bootstrap or Foundation.

Bootstrap is mostly used in those solutions where a website runs on all kinds of devices including laptops and smart phones while Foundation is more suitable for small devices with limited storage and computing power like smart phones and tablets.

Both of these technologies are based on CSS3 and therefore by applying a simple CSS class, we can create different layouts and components. For example, by applying a few Bootstrap CSS classes, we can create split buttons:

CSS3

High level responsive frameworks like Bootstrap or Foundation use CSS3 media queries and Unobtrusive JavaScript to render web pages on devices with different sizes and capabilities. Consider for example, Style.css below is applied only when the device's maximum width is 480px and resolution is 163 DPI.

<link rel="stylesheet" type="text/css" 
       media="screen and (max-device-width: 480px) and (resolution: 163dpi)" 
       href="Style.css" />

CSS3 media queries are at the heart of making web apps responsive. Media queries uses media features like device-width, device-height, orientation, device-aspect-ratio in CSS file to develop highly responsive web apps. For example, Device.css below will be applied only to those devices which have colored screen in portrait orientation. This gives great control over what we want to show (or don't want to show) on a particular device, orientation and resolution.

<link rel="stylesheet" media="screen and (color) and (orientation: portrait)" href="Device.css" />

Using CSS selectors one can write very granular CSS rules. In the presence of Bootstrap, Foundation and tons of responsive themes you may not be using CSS3 directly, but remembering following CSS selectors will greatly help in understanding what you could do with CSS3 and jQuery. Checkout these interesting examples:

// * is a universal selector. Make all links
// red on hover that are grandchild of any <div>
div * a:hover { color: red;}

// You can apply rules to group
h1, h2, h3 { color: blue;}

// Select elements that have any attribute.
// This rule will apply to all h1 which have 'article' attribute defined
h1[article]{ color:blue;}

// This rule will apply to all h1 where article="title"
h1[article="title"]{ color:blue;}

// This rule will apply to all h1 where article contains space
// separated values one of which is exactly equal to "title"
// e.g. <h1 article="top title text">Main Title</h1>
h1[article~="title"]{ color:blue;}

// Select all h1 where article attribute begins with top
h1[article^="top"]{ color:blue;}

// Select all h1 where article attribute ends with top
h1[article$="top"]{ color:blue;}

// Select all h1 where article attribute has a sub string top
h1[article*="top"]{ color:blue;}

// Select all h1 where article attribute contains hypen separated values which begins with top
h1[article|="top"]{ color:blue;}

HTHM and therefore DOM is hierarchal, so we could select elements based on parent-child relationship (e.g., first child), their relative position (e.g., n - 1 child where n is the last child) and element type (e.g., img, div).

// Select specific child or specfic type of child
div:nth-child(n-1)
div:nth-last-child(n-1)
img:nth-of-type(n-1)
img:nth-last-of-type(n-1)
div:first-child
div:last-child
img:first-of-type
div:last-of-type
div:only-child
img:only-of-type
div:empty

// Select all links with class="external" in a specfic state
a.external:link {color: cyan;}
a.external:visited{color: grey;}
a.external:active{color: red;}
a.external:hover{color: white;}

//Selection based on element state
input:focus {color: red;}
p:target{color: red;}
input:lang(en-US){color: red;}
input:enabled{color: red;}
input:disabled{color: red;}
input:checked {color: red;}

// Select first line first letter of <p> where class="main"
p.main::first-line { text-transform: uppercase }

// Select first letter of <p> where class="main"
p.main::first-letter { text-transform: uppercase }

// Select elements before or after p
p::before { text-transform: uppercase }
p::after { text-transform: uppercase }

// Select all div with CSS class = "sidebar"
div.sidebar{color: red;}

// Select div with id="socialbar"
div#socialbar{color: red;}

// Select links that are not visited
a:not(:visted) {color: red;}

// Select all p that are immidiate child of any div
div p{color: red;}

// Select all p that are child of divor any of its children
div > p{color: red;}

// Select all p that areadjacentsibling of div
div + p{color: red;}

// Select all p that are sibling ofdiv ~ p{color: red;}

As you can imagine a CSS3 stylesheet can get very complex and hard to manage, so there is a need of CSS preprocessors like LESS and SASS. These preprocessors extend CSS with dynamic behavior such as variables, mixins, operations, and functions. Bootstrap uses LESS to create complex CSS rules. For example, you could define a variable and the resulting CSS will expand that variable.

Figure - LESS variable

One of the problems with CSS development is that different browsers have different "default styles" for HTML elements like <sup>, <sub>, <h1>, <h2>, <h3>, etc. This causes CSS to work in a browser but break in another. To avoid this problem,  we could use Reset.css or Normalize.css. With Reset.css, the CSS developer sets HTML elements to some default value. However it is a less preferred method as compared to Normalize.css because Reset.css unstyles every HTML element and removes some useful defaults. For example, the following reset will set h1, h2, h3 and h4 font-size to 100%, which is not really useful in most cases.

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}

To better address the browser defaults problems, Normalize.css corrects styling and makes it consistent in every browser.

CSS developers are also challenged by the various browser modes. Modern browsers support two modes for parsing HTML and applying CSS. One is called Strict or Standard Mode in which the developer specifies the exact DOCTYPE. In Strict mode, the browser uses W3C specification to layout a web page.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

The other mode is called Quirks Mode, which the browser uses when no DOCTYPE tag is used. In this mode the browser uses its non-standard rules to layout a document. One of such a non-standard rule is Box Model. CSS developers face a good deal of problems due to differences in the Box Model implementation in various browsers.

A Box Model is fundamental to the layout a web page. A web browser creates a hierarchical tree of HTML elements (i.e., DOM) in the form of rectangle boxes as shown below.

Figure - W3C box model

Each HTML element like <h1>, <h2>, <img>, <p>, <br> gets its box.

Figure - Padding, Border, and Margin

Earlier versions of browsers, especially IE 5.x, use a different Box Model to calculate the width as specified by W3C. To render such pages, Quirks mode is used. The difference between W3C and the IE box model could be seen in the figure below. You can see the W3C width specification does not include padding and border.

Figure - IE vs. W3C Box Model

Currently IE10 supports various quirk modes to support old pages. To enable IE5 quirk mode for legacy pages in IE10, use the following <meta> tag in <head> before any script.

<meta http-equiv="X-UA-Compatible" content="IE=5">

Figure - IE 10 Qurik Modes

For all new pages do not use quirk mode. Instead use Standard or Strict mode by specifying correct DOCTYPE. Also CSS3 provides an excellent way to overcome the Box Model issue by using the box-model attribute. If box-model is set to border-box then element width includes padding and borders.

p { box-sizing:border-box; width:100px; padding:10px; border:10px solid blue; }

Unobtrusive JavaScript

Discussion of Responsive Web App does not complete without Unobtrusive JavaScript. Unobtrusive JavaScript is an umbrella term used to write JavaScript that works on large number of devices with different capabilities and one that is easy to maintain. What this really means in one way to separate JavaScript from HTML as shown below.

// 1- Obtrusive JavaScript
<input type="text" name="date" onchange="validateDate()" />
<pre lang="jscript">function() {
    document.getElementById('date').onchange = validateDate;
} 
// 2- Unobtrusive JavaScript  
<input type="text" name="date" id="date" />
window.onload = function() {window.onload = function() {
    document.getElementById('date').onchange = validateDate;
};  

In sample 2 above, validateDate() is decoupled from HTML using element id 'date'. We can now put JavaScript in separate file(s) to increase code maintainability. It is far easier to fix issues, add new features and introduce polyfills in modular JavaScript files.

Browsers are catching up with HTML 5 and CSS 3 specification and dispensing more and more support with every new version. To fill up gaps in browser functionality polyfill is used. A polyfills is JavaScript that provides missing functionality in browser. For example, Firefox 23, 24, and 25 do not support HTML5 Web SQL Storage and IE8 & 9 can not do CSS3 Transitions. In such cases, a polyfills provides a fallback for these browsers and supply necessary functionality. A polyfill helps in graceful degradation of JavaScript, which is another guideline of writing Unobtrusive JavaScript. There are dozens of polyfills available at the time of writing. You can check browser support for HTML5 and CSS3 at caniuse.com.

Also Modernizr is a JavaScript library that detects HTML5 and CSS3 features in the user’s browser. Modernizr supports dozens of tests, and optionally includes YepNope.js for conditional loading of external .js and .css resources. This is specifically useful in loading polyfills. For example, following will load geo.js only if the browser supports geo location feature else a polyfill geo-polyfill.js will be loaded.

Modernizr.load({
  test: Modernizr.geolocation,
  yep : 'geo.js',
  nope: 'geo-polyfill.js'
}); 

We can easily predict that an unobtrusive JavaScript results in fast page loads due to a smaller HTML file. It is possible to further increase page load speed by JavaScript minification, compression, and deferred loading which is very important for contents targeted for smart phones, tablets and other devices with limited bandwidth, storage and computation power.

Minification is a process to combine multiple CSS or JavaScript files, remove unnecessary whitespace and comments. There are online tools available which not only minify CSS or JavaScript but also serve them with gzip encoding and optimal client-side cache headers. Such JavaScript/CSS files could also use CDN and browser cache effectively to further shorten page load time.

Figure - What is Deferred Page Loading?

Heavy usage of JavaScript libraries resulted in deferred page loading techniques. A webpage won't display until all your JavaScript is downloaded, this causes severe delays unless it is deferred properly. A page will load faster if we download 'certain' JavaScript files on page load event like click events .js file. A simple JavaScript could defer loading easily.

<script type="text/javascript">
 // Add a script element as a child of the body
 function downloadJSAtOnload() {
     var element = document.createElement("script");
     element.src = "deferredfunctions.js";
     document.body.appendChild(element);
 }
 // Check for browser support of event handling capability
 if (window.addEventListener)
     window.addEventListener("load", downloadJSAtOnload, false);
 else if (window.attachEvent)
     window.attachEvent("onload", downloadJSAtOnload);
 else window.onload = downloadJSAtOnload;
</script> 

jQuery is a popular library that helps in writing unobtrusive JavaScript. Consider for example, it is really intuitive in jQuery if you want to chain together the selection of a paragraph id p1, changing its color to red and then sliding it up and down. Try this example here.

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function()
  {
  $("button").click(function(){
    $("#p1").css("color","red").slideUp(2000).slideDown(2000);
  });
});
</script>
</head>
<body>
<p id="p1">jQuery is fun!!</p>
<button>Click me</button>
</body>
</html>  

Not only this, jQuery simplifies AJAX calls, DOM events attachment, and element selection. It uses Sizzle as its CSS selector engine. Sizzle supports almost all CSS3 selectors discussed earlier in this article.

Writing JavaScript could be challenging for new comers as it has has type coercion. Numbers can become strings, strings can become numbers, and arrays can become strings. Checkout below why empty string - 1 = -1 in JavaScript:

var x = "" - 1; // x will set to -1. Empty string = 0
var a = []; // [] = 0 in JavaScript
++a; // a will set to 1   

To avoid type coercion and to increase code maintainability, unobtrusive JavaScript guidelines prefer === and !== operators as compared to their 'evil twins' == and !=. === does not convert type while == converts type before comparison.

Consider some difficult to guess JavaScript conditions in code below:

undefined == null // return true
NaN == NaN // return false
2 == "2" // return true because 2 == ToNumber("2")
false == 0 // return true because ToNumber(false) == 0
true == 1 // return true because ToNumber(true) == 1
true != 2 // return false because ToNumber(true) != 2

While comparing objects to primitive type JavaScript calls valueOf() if primitive type is number else it calls toString().

// If object defines valueOf it is used in comparison
// else toString() is used
var amt = {
    valueOf: function () {
        return 10;
    }
};
amt == 10 // return true because amt.valueOf() == 10 
var amt = {
    toString: function() {
        return 20;
    }
};
amt == 10 // return true because amt.toString() != 10

// You can define a variable by name 'undefined' 
var undefined = 10;
alert(undefined);

// if (arg == undefined) comapre two var 
// while if(typeof arg != 'undefined') checks if arg is defined
var arg = 0;
if(typeof arg == 'undefined')
    alert ('arg is not defined');
else
    alert ('arg is defined');

alert(Math.min() < Math.max()); // returns false
alert(typeof NaN) // returns number.   

Figure - JsFiddle in Action

JavaScript coercion, loose type system and OOP syntax (which is different as compared to popular languages like Java, C#) has created programmer's outcry which could be felt here. JsFiddle is one of popular ways to play with JavaScript and Jslint to measure its quality. There is even an online tool to beautify your JavaScript code.

Figure - Chrome Developer Tools

Chrome provides a built-in support to debug JavaScript and CSS and it comes very handy when debugging and tuning JavaScript.

JavaScript coercion made people to write all kinds of transcompilers to convert language of your choice into JavaScript. Some of them are CoffeeScript, Microsoft TypeScript, Objective-J, Google DART and many others.

Among these Microsoft TypeScript is interesting being a strict superset of JavaScript (like C++ is for C) and probably the future of JavaScript i.e. ECMAScript Harmony (6th Edition). TypeScript supports OO syntax and is strongly typed, so code completion and compile time type checking is possible.

// TypeScript 
class Greeter { 
    greeting: string; 
    constructor(message: string) { 
        this.greeting = message; 
    } 
    greet() { 
        return "Hello, " + this.greeting; 
    } 
}

// JavaScript 
var Greeter = (function () {
function Greeter(message) {
    this.greeting = message;
}
Greeter.prototype.greet = function () {
    return "Hello, " + this.greeting;
};
return Greeter;
})();

You can see it is far easier to comprehend TypeScript OO syntax as compared to JavaScript.

Google DART on the other hand is a totally new language which compiles into JavaScript. Like with other preprocessors, debugging DART generated JavaScript and using existing JavaScript libraries pose a challenge.

CoffeeScript, inspired by Python/Perl, is like TypeScript and and it compiles into JavaScript. It is widely used as an alternative of JavaScript and claims to reduce code to 1/3. Checkout this comparison of Coffeescript vs. Typescript vs. Dart.

Figure - Selling “Samy is my hero” T-shirts

With the popularity of JavaScript, Cross Side Scripting (or XSS) and Cross-site Request Forgery (or CSRF) attacks have become common. There are many people who may still remember Samy Worm at MySpace that shut down the popular social networking site for two and half hour when its creator was able to receive 1 Million friend requests in less than 24 hours.

Open Web Application Security Project provides a good starting point to sanitize your application against XSS and CSRF attacks. Microsoft provides Microsoft Web Protection Library to encode user input, including HTML, HTML attributes, XML, CSS, and JavaScript. One way to test your site for XSS and CSRF issues is to use BEEF.

But as a summary, never put untrusted data at following locations and 'escape' with some library like Microsoft Web Protection Library:

<!--HTML Body -->
<span>...NEVER PUT UNTRUSTED DATA HERE...</span>
<script>...NEVER PUT UNTRUSTED DATA HERE...</script> directly in a script
<!--...NEVER PUT UNTRUSTED DATA HERE...--> inside an HTML comment
<div>...NEVER PUT UNTRUSTED DATA HERE...</style> directly in CSS
<input type="text" name="fname" value="...NEVER PUT UNTRUSTED DATA HERE.../>
<a href="/site/search?

value=...NEVER PUT UNTRUSTED DATA HERE...">clickme</a>

<!--Untrusted URL in a SRC or HREF attribute -->
<iframe src="UNTRUSTED URL" />

<!--CSS Value -->
<div style="width: ...NEVER PUT UNTRUSTED DATA HERE...;">Selection</div>

<!--JavaScript Variable -->
<script>var currentValue='...NEVER PUT UNTRUSTED DATA HERE...';</script>
<script>someFunction('...NEVER PUT UNTRUSTED DATA 

HERE...');</script>

<!--HTML Body -->
<div>UNTRUSTED HTML<div>

<!--DOM XSS -->
<script>document.write("UNTRUSTED INPUT: " + document.location.hash);<script/>

While XSS attacks on smart phones and tablets could be minimized using the above practices, there are reports that Apple, Google, and Microsoft are building huge databases for their location services by illegally tracking user location. :)

What's Next

In the next part I will discuss JavaScript OO and popular JavaScript frameworks like Knockout, Bootstrap, Angular etc., and what problems they solve. I will also discuss HTML 5 and its role in the development of Responsive Web App.

After completing this discussion we will see what are the challenges of using native and hybrid frameworks for solutions targeted for devices with limited storage and computing power. Lastly, we will cover Server Code Base and challenges and solutions over there, and the role of intranet infrastructure on the scalability, availability, and performance of a web application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here