Single Page App Architecture
What Are Single page apps?
A single page app is an app that runs entirely on the client-side (ie. browser) with the server providing services such as data, processing, auth etc.
The key difference to SPA's is that unlike the old model, the application does not refresh the page in order to navigate or instantiate new views. Generally speaking all core assets are loaded by index.html and further assets are lazy loaded when required.
Why Would I want to build one?
The main reason to build an SPA is if your existing or planned backend is a pure data service. Basically if you're serving nothing but JSON or XML or some other form of raw data that needs to be digest and displayed, SPA really are a way to go.
Another reason might be that you're planning to take the jump and build your backend with node and would like to share code.
With that said, you should think hard about the decision to go SPA because once you're in the groove, changing to another arch generally means a rewrite, and then your boss will really start to appreciate your talents.
MV-whatever
There are 3 major design patterns for organizing SPAs
-
MVC - Model View Controller
- MVP - Model View Presenter
- MVVM - Model View ViewModel
As you can see, the difference comes down to the glue layer between the model and view; And although the patterns vary on their implementation, the fundamentals are all the same.
The key is that it's not that choice itself that matters but that it fits your apps' particular requirements.
Model-View-Controller
Model-view-viewmodel
Folder structure
The key here is to keep it simple, the last thing you want is to muck about with relative paths and configuration with loaders like RequireJS.
Modules
Browserify vs RequireJS vs Custom
Modularizing your code is not a question, however, which system to use definitely is. The loaders mentioned above are one way to go, but sometimes, they're simply overkill. Sometimes all you really need is a few <script src=""> tags, and bootstrap file to kick off the party.
As always your requirements should be dictating the right path to choose.
The module pattern
The module pattern is good emulating privacy in Javascript but it's also a fundamental building block for the facade pattern we'll look at next. That IIFE let's you easily hide any implementation details and only export the desired interface.
(function(app) {
var hiddenObj = {};
hiddenObj.method1 = function() {};
hiddenObj.method2 = function() {};
hiddenObj.method3 = function() {};
app.export = {
exported: hiddenObj.method1,
exported1: hiddenObj.method2
};
})(simpleBB);
FACADES and mediators
Facade - Comp Sci word used to describe the interface that a module exposes to it's consumers.
Mediator - A word used to describe what is essentially a simple event bus object used for communication between the app core and modules and between modules themselves. This object often has a pub/sub facade.
The mediator
var mediator = (function(){
var subscribe = function(channel, fn){},
publish = function(channel){};
return {
channels: {},
publish: publish,
subscribe: subscribe,
installTo: function(obj){
obj.subscribe = subscribe;
obj.publish = publish;
}
};
}());
Hierarchical structures
When it comes to dealing with hierarchical structure (and you will) the framework should really be lifting you up here and providing facilities for making this easier. Even though I love backbone, one of it's major lacking features is any kind of guidance or support for hierarchy. Plugins like Backbone-Relational have emerged, but even now most of them are simply a pain to deal with.
Interestingly, Angular is one of the only frameworks where dealing with it is really not so bad, which probably has something to do with those magic bindings and plain object models. Either way, be prepared to suffer and maybe even write your own solution.
Knowing your history
The first rule of the dealing with the back button is you don't break the back button.
The second rule of dealing with the back button is you don't break the back button.
There is no third rule.
Users have a significant expectation of the back button, and that expectation is 'bring me back to where I was before'. Your routing structure and view handling code needs to be designed in such a way that the back button either brings up the previous view or reconstructs previous state.
The environment
Grunt, Yeoman, Bower, Mocha
Don't reinvent the wheel! The 4 tools mentioned above will help you scaffold out your app, build it for production, manage your dependencies and test your application.
In the rare scenario where one of those tools doesn't do what you need, Grunt, Yeoman and Mocha are massively flexible and support plugins to extend or improve their functionality.
The war on frameworks
MVC Frameworks:
MVP Frameworks:
MVVM Frameworks:
The war on frameworks
MVC Frameworks:
MVP Frameworks:
MVVM Frameworks:
Crawlability and SEO
So you have a beautiful app, it's tested, well written, everything works, but then Google Bot comes around and everything goes down the drain. At the time of writing, most crawlers can't properly execute your js and crawl the resulting DOM.
Headless browser to the rescue! Spin up an instance of PhantomJS and have any connection from known crawlers redirect to it. Is this ideal? No. Is it a workaround/hack/dirty/costly/unseemly/resource hog/adjective? Yes. Will it get your boss off your case? Maybe.
alternatives: ZombieJS, HtmlUnit
The land of Mobile
The general rule for ensuring app functionality on mobile is testing, testing, testing. There's simply no way around it.
A fairly good pattern to follow is to ensure your code is as standard ES5 as possible, and shimming anything that might be missing.
Mobile is hard enough as it is, and the best protection is a good test suite and a killer integrated testing solution like TravisCI and friends.