2. Introduction
What are we looking at today?
Scalable Application
Design Patterns Patterns in JavaScript
Architecture
Thursday, 6 October 2011
3. About Me
Some quick info.
• JavaScript & UI Developer @AOL
• Member of the jQuery core [Bugs/Docs/
Learning] teams
• Blogger [AddyOsmani.com/ScriptJunkie]
• Author ‘Essential JavaScript Design Patterns’
Thursday, 6 October 2011
4. We used to make these:
Thursday, 6 October 2011
5. Which now make awesome pizza-cutters
Thursday, 6 October 2011
7. We’re Individuals
We all like doing things a little bit differently.
“You have your way. I have my way. As for the right way, the correct way,
and the only way, it does not exist.”
- Friedrich Nietzsche
Thursday, 6 October 2011
9. We all do things differently
Each of us have preferences for how we approach the below:
Solving problems Structuring solutions Solving scalability
Thursday, 6 October 2011
10. Great but can lead to..
serious problems when working on code to be used by others.
Inconsistent solutions Inconsistent architecture Dif cult refactoring
Thursday, 6 October 2011
11. A lot like how most Stormtroopers know that there’s a time,
a place and a way to wear your uniform..and others
completely ignore this.
Thursday, 6 October 2011
13. Design Patterns
Reusable solutions that can be applied to commonly
occurring problems in software design and architecture.
“We search for some kind of harmony between two intangibles: a form we have not yet
designed and a context we cannot properly describe’
- Christopher Alexander, the father of design patterns.
Thursday, 6 October 2011
14. They’re proven
Patterns are generally proven to have successfully solved problems in the past.
Reliable Re ect Represent
Solid
approaches experience insights
Thursday, 6 October 2011
15. They’re reusable
Patterns can be picked up, improved and adapted without great effort.
Out-of-the-box Incredibly exible Easily adapted
solutions
Thursday, 6 October 2011
17. They’re expressive
Patterns provide us a means to describing approaches or structures.
Common vocabulary Problem agnostic Valuable as they can
for expressing cut down on problems
solutions elegantly
Thursday, 6 October 2011
19. Module Pattern
Interchangeable single-parts of a larger system that can be
easily re-used.
“Anything can be de ned as a reusable module”
- Nicholas Zakas, author ‘Professional JavaScript For Web Developers’
Thursday, 6 October 2011
20. Stepping stone: IIFE
Immediately invoked function expressions (or self-executing anonymous functions)
(function() {
// code to be immediately invoked
}()); // Crockford recommend this way
(function() {
// code to be immediately invoked
})(); // This is just as valid
(function( window, document, undefined ){
//code to be immediately invoked
})( this, this.document);
(function( global, undefined ){
//code to be immediately invoked
})( this );
Thursday, 6 October 2011
23. Privacy In Modules
There isn’t a true sense of ‘privacy’ inside JavaScript.
Variables & Methods Variables & Methods
No Access Modi ers
can’t be ‘public’ can’t be ‘private’
Thursday, 6 October 2011
24. Simulate privacy
The typical module pattern is where immediately invoked function expressions (IIFEs) use
execution context to create ‘privacy’. Here, objects are returned instead of functions.
var basketModule = (function() {
var basket = []; //private • In the pattern, variables
return { //exposed to public declared are only
addItem: function(values) { available inside the
basket.push(values); module.
},
getItemCount: function() {
return basket.length; • Variables de ned within
}, the returning object are
getTotal: function(){ available to everyone
var q = this.getItemCount(),p=0;
while(q--){
p+= basket[q].price;
• This allows us to simulate
privacy
}
return p;
}
}
}());
Thursday, 6 October 2011
25. Module Pattern: Dojo
Dojo attempts to provide 'class'-like functionality through dojo.declare, which can be used for amongst
other things, creating implementations of the module pattern. Powerful when used with dojo.provide.
//traditional way
var store = window.store || {};
store.basket = store.basket || {};
//using dojo.setObject (with basket as a module of the store namespace)
dojo.setObject("store.basket.object", (function() {
var basket = [];
function privateMethod() {
console.log(basket);
}
return {
publicMethod: function(){
privateMethod();
}
};
}()));
Thursday, 6 October 2011
26. Module Pattern: jQuery
In the following example, a library function is de ned which declares a new library and automatically binds
up the init function to document.ready when new libraries (ie. modules) are created.
function library(module) {
$(function() {
if (module.init) {
module.init();
}
});
return module;
}
var myLibrary = library(function() {
return {
init: function() {
/*implementation*/
}
};
}());
Thursday, 6 October 2011
27. Module Pattern: YUI
A YUI module pattern implementation that follows the same general concept.
YAHOO.store.basket = function () {
//"private" variables:
var myPrivateVar = "I can be accessed only within YAHOO.store.basket .";
//"private" method:
var myPrivateMethod = function () {
YAHOO.log("I can be accessed only from within YAHOO.store.basket");
}
return {
myPublicProperty: "I'm a public property.",
myPublicMethod: function () {
YAHOO.log("I'm a public method.");
//Within basket, I can access "private" vars and methods:
YAHOO.log(myPrivateVar);
YAHOO.log(myPrivateMethod());
//The native scope of myPublicMethod is store so we can
//access public members using "this":
YAHOO.log(this.myPublicProperty);
}
};
}();
Thursday, 6 October 2011
28. Better: AMD
Take the concept of reusable JavaScript modules further with the
Asynchronous Module De nition.
Mechanism for de ning Stepping-stone to the
Non-blocking, parallel
asynchronously loadable module system proposed
loading and well de ned.
modules & dependencies for ES Harmony
Thursday, 6 October 2011
29. AMD: de ne()
de ne allows the de nition of modules with a signature of
de ne(id /*optional*/, [dependencies], factory /*module instantiation fn*/);
/* wrapper */
define(
/*module id*/
'myModule',
/*dependencies*/
['foo','bar','foobar'],
/*definition for the module export*/
function (foo, bar, foobar) {
/*module object*/
var module = {};
/*module methods go here*/
module.hello = foo.getSomething();
module.world = bar.doSomething();
/*return the defined module object*/
return module;
}
);
Thursday, 6 October 2011
30. AMD: require()
require is used to load code for top-level JS les or inside modules for
dynamically fetching dependencies
/* top-level: the module exports (one, two) are passed as
function args to the callback.*/
require(['one', 'two'], function (one, two) {
});
/* inside: complete example */
define('three', ['one', 'two'], function (one, two) {
/*require('string') can be used inside the function
to get the module export of a module that has
already been fetched and evaluated.*/
var temp = require('one');
/*This next line would fail*/
var bad = require('four');
/* Return a value to define the module export */
return function () {};
});
Thursday, 6 October 2011
31. Alternative: CommonJS
Another easy to use module system with wide adoption server-side
CommonJS
Format widely accepted Competing standard. Tries
Working group
on a number of server-side to solve a few things AMD
designing, prototyping,
platforms (Node) doesn’t.
standardizing JS APIs
Thursday, 6 October 2011
32. CommonJS Modules
They basically contain two parts: an exports object that contains the objects a
module wishes to expose and a require function that modules can use to import
the exports of other modules
/* here we achieve compatibility with AMD and CommonJS
using some boilerplate around the CommonJS module format*/
(function(define){
define(function(require,exports){
/*module contents*/
var dep1 = require("foo");
var dep2 = require("bar");
exports.hello = function(){...};
exports.world = function(){...};
});
})(typeof define=="function"? define:function(factory){factory
(require,exports)});
Thursday, 6 October 2011
33. Better alternative: Universal Module De nition
De ning modules that can work anywhere (CommonJS environments such as
clients, servers; with script loaders etc). Thx to @KitCambridge for this version.
(function (root, Library) {
// The square bracket notation is used to avoid property munging by the Closure Compiler.
if (typeof define == "function" && typeof define["amd"] =="object" && define["amd"]) {
// Export for asynchronous module loaders (e.g., RequireJS, `curl.js`).
define(["exports"], Library);
} else {
// Export for CommonJS environments, web browsers, and JavaScript engines.
Library = Library(typeof exports == "object" && exports|| (root["Library"] = {
"noConflict": (function (original) {
function noConflict() {
root["Library"] = original;
// `noConflict` can't be invoked more than once.
delete Library.noConflict;
return Library;
}
return noConflict;
})(root["Library"])
}));
}
})(this, function (exports) {
// module code here
return exports;
});
Thursday, 6 October 2011
34. ES Harmony Modules
A module format proposed for EcmaScript Harmony with goals such as static scoping,
simplicity and usability.
// Basic module
module SafeWidget {
import alert from Widget;
var _private ="someValue";
// exports
export var document = {
write: function(txt) {
alert('Out of luck, buck');
},
...
};
}
// Remote module
module JSONTest from 'http://json.org/modules/json2.js';
Thursday, 6 October 2011
35. Facade Pattern
Convenient, high-level interfaces to larger bodies of code
that hide underlying complexity
“When you put up a facade, you're usually creating an outward appearance which conceals a
different reality. Think of it as simplifying the API presented to other developers”
- Essential JavaScript Design Patterns
Thursday, 6 October 2011
37. Example
A higher-level facade is provided to our underlying module, without directly exposing methods.
var module = (function() {
var _private = {
i:5,
get : function() {
console.log('current value:' + this.i);
},
set : function( val ) {
this.i = val;
},
run : function() {
console.log('running');
},
jump: function(){
console.log('jumping');
}
};
return {
facade : function( args ) {
_private.set(args.val); Limited public view of functionality.
_private.get();
if ( args.run ) { Differs greatly from the reality of the
_private.run(); implementation.
}
}
}
}());
module.facade({run: true, val:10}); //outputs current value: 10, running
Thursday, 6 October 2011
38. A Facade
A structural pattern found in many JavaScript libraries and frameworks (eg. jQuery).
Simpli es usage by Hides the inner-
This lets you be more
encouraging use of workings of a library.
creative behind the
a limited interface for Allows implementation
scenes.
interaction to be less important.
Thursday, 6 October 2011
39. Mediator Pattern
Encapsulates how disparate modules interact with each
other by acting as an intermediary
“Mediators are used when the communication between modules may be complex, but
is still well de ned”
- Essential JavaScript Design Patterns
Thursday, 6 October 2011
40. Air Traf c Control
I always nd this mediator analogy helps when discussing this pattern:
The tower handles All communication done Centralised controller
what planes can take from planes to tower, is key to this success.
off and land not plane to plane Similar to mediator.
Thursday, 6 October 2011
41. A Mediator
Promotes loose coupling. Helps solve module inter-dependency issues.
Allow modules to Noti cations can be Typically easier to add or
broadcast or listen for handled by any number of remove features to loosely
noti cations without modules at once. coupled systems like this.
worrying about the system.
Thursday, 6 October 2011
42. Mediator Implementation
One possible implementation, exposing publish and subscribe capabilities.
var mediator = (function(){
var subscribe = function(channel, fn){
if (!mediator.channels[channel])mediator.channels[channel] = [];
mediator.channels[channel].push({ context: this, callback:fn });
return this;
},
publish = function(channel){
if (!mediator.channels[channel]) return false;
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0, l = mediator.channels[channel].length; i <l; i++) {
var subscription = mediator.channels[channel][i];
subscription.callback.apply(subscription.context,args);
}
return this;
};
return {
channels: {},
publish: publish,
subscribe: subscribe,
installTo: function(obj){
obj.subscribe = subscribe;
obj.publish = publish;
}
};
}());
Thursday, 6 October 2011
43. Example
Usage of the implementation from the last slide.
//Pub/sub on a centralized mediator
mediator.name = "tim";
mediator.subscribe('nameChange', function(arg){
console.log(this.name);
this.name = arg;
console.log(this.name);
});
mediator.publish('nameChange', 'david'); //tim, david
//Pub/sub via third party mediator
var obj = { name: 'sam' };
mediator.installTo(obj);
obj.subscribe('nameChange', function(arg){
console.log(this.name);
this.name = arg;
console.log(this.name);
});
obj.publish('nameChange', 'john'); //sam, john
Thursday, 6 October 2011
44. Scalable Application
Architecture
Strategies for decoupling and future-proo ng the structure
of your application.
Thanks to Nicholas Zakas, Rebecca Murphey, John Hann, Paul Irish, Peter Michaux and Justin
Meyer for their previous work in this area.
Thursday, 6 October 2011
46. De ne a ‘large’ JS app.
Large-scale JavaScript apps are non-trivial
applications requiring signi cant developer effort
to maintain, where most heavy lifting of data
manipulation and display falls to the browser.
Thursday, 6 October 2011
47. Some Examples
Google’s GMail
Thursday, 6 October 2011
48. Some Examples
The Yahoo! Homepage
Thursday, 6 October 2011
49. Some Examples
AOL Mail / Phoenix
Thursday, 6 October 2011
50. Current Architecture
If working on a signi cantly large JavaScript app,
remember to dedicate suf cient time to planning the
underlying architecture that makes the most sense.
It’s often more complex than we initially think.
Thursday, 6 October 2011
52. Your Current Architecture
might contain a mixture of the following:
Custom Widgets
Modules
An Application Core
MVC (Models/Views/Controllers)
JavaScript Libraries & Toolkits
Thursday, 6 October 2011
53. Possible Problems
The last slide contains some great architectural components, but used non-
optimally they can come with a few problems:
Can single modules Can single modules
How much of this is exist on their own be tested
instantly re-usable? independently? independently?
Thursday, 6 October 2011
54. Possible Problems
Some further concerns:
How much do Is your application If speci c parts of
modules depend on tightly coupled? your app fail, can it
others in the system? still function?
Thursday, 6 October 2011
55. Think Long-Term
What future concerns haven’t been factored in to this architecture?
• You may decide to switch from using jQuery to Dojo
or YUI for reasons of performance, security or design
• Libraries are not easily interchangeable and have
high switching costs if tightly coupled to your app
Thursday, 6 October 2011
56. Ask Yourself
This is important.
If you reviewed your architecture right
now, could a decision to switch
libraries be made without rewriting
your entire application?
Thursday, 6 October 2011
57. A Solution
Fixing our architecture with JavaScript design patterns.
“The only difference between a problem and a solution is that people understand the
solution.’
- Charles F. Kettering
Thursday, 6 October 2011
60. Let’s Think.
What do we want?
Functionality broken Framework or library
Loosely coupled down into smaller agnostic. Flexibility to
architecture independent modules change in future.
Thursday, 6 October 2011
61. Some More Ideas.
How might we achieve this?
An intermediate layer
Single modules speak
interprets requests. Prevents apps from
to the app when
Modules don’t access falling over due to errors
something interesting
the core or libraries with speci c modules.
happens
directly.
Thursday, 6 October 2011
62. The Facade
An abstraction of the core, it listens out for interesting
events and says ‘Great. What happened? Give me the
details’. It also acts as a permissions manager. Modules
only communicate through this and are only able to do
what they’ve been permitted to.
Thursday, 6 October 2011
63. The Application Core
Manages the module lifecycle. It reacts to events passed
from the facade and starts, stops or restarts modules as
necessary. Modules here automatically execute when
loaded.
Thursday, 6 October 2011
64. Modules
Unique blocks of functionality for your application. They
inform the application when something interesting
happens. Don’t talk to each other directly, only concerned
with publishing events of interest.
Thursday, 6 October 2011
65. Aura: A Preview
Enough talk! Let’s take a look at some real code.
Aura is a framework I’m building at AOL that provides a boilerplate for one way to approach
implementing this architecture. It will be released for open-source consumption once stable.
Thursday, 6 October 2011
66. Aura: Core
The Mediator / Application Core
• Swappable Mediator with a light wrapper around a
speci c JavaScript library
• Ability to start and stop modules
• By default it comes with wrappers for both Dojo
and jQuery, with core syntax that resembles the
latter
Thursday, 6 October 2011
67. Aura: Core
How does this work?
• Accessing this wrapper, the facade doesn’t care
what framework has been slotted in. It works with
the abstraction
• Behind the scenes, arguments are mapped to the
relevant jQuery or dojo methods and signatures for
achieving speci c behaviour
Thursday, 6 October 2011
68. Aura: Core
A sample of the method signatures and namespaces supported
// some core methods
core.start(module_id); // define and initialise a module
core.startAll(); // define, initialise all modules
core.stop(module_id); // stop a specific module
core.stopAll(); // stop all modules
core.destroy(module_id); // destroy a specific module
core.destroyAll(); // destroy all modules
// core namespaces
core.events // bind, unbind etc.
core.utils // type checking.
core.dom // css, DOM manipulation
Thursday, 6 October 2011
69. Aura: Core.dom > Chaining, CSS
Chaining and CSS Style Manipulation are both supported. Behind the scenes, this works with both jQuery
and Dojo, providing a single abstracted API that’s familiar to users.
// Chaining and CSS style manipulation
aura.core.dom.query('body').css({'background':'#1c1c1c'});
aura.core.dom.query('#search_input').css({'background':'blue'}).css({'color':'pink'});
aura.core.dom.query('#search_button').css({'width':'200','height':'100'});
// Manipulating styles within a context
aura.core.dom.css('body', {'background':'red'});
aura.core.dom.css('#shopping-cart',{'color':'green','background':'yellow'});
aura.core.dom.css('#product-panel li', {'background':'purple'});
// Passing in DOM nodes also works
var test = aura.core.dom.query('#shopping-cart'); //.query should handle this.
aura.core.dom.css(test, {'background':'purple'});
Thursday, 6 October 2011
70. Aura: Core.dom > Attribs, Anim
Attribute manipulation and animation are also abstracted using an API similar to jQuery. Remember, with
Dojo this actually maps arguments back to the relevant Dojo methods needed to achieve the task.
// Get and set attributes
console.log(aura.core.dom.query('#seach_input').attr('title','foobar'));
console.log(aura.core.dom.query('#search_input').attr('title'));
// Passing in DOM nodes
var q = aura.core.dom.query('#shopping-cart');
console.log(aura.core.dom.attr(q, 'id'));
// Animation support
aura.core.dom.animate('#product-panel li', { width: 400, height:20}, 5000);
aura.core.dom.animate('button', {width: 200, duration: 100});
aura.core.dom.animate('p', {width:20, height:40, padding:10,duration:200});
Thursday, 6 October 2011
71. Aura: Core.dom > Create, Ajax
Similarly, element creation and ajax are also supported in the abstracted core interface.
// Element creation
var el = aura.core.dom.create("a", {
href: "foo.html",
title: "bar!",
innerHTML: "link"
},
'body');
// XHR/Ajax requests (deferred support coming soon)
aura.core.dom.ajax({
url:'index.html',
type:'get', //post, put
dataType: "text",
success:function(data){
console.log('success');
},
error:function(){
console.log('error');
}
});
Thursday, 6 October 2011
72. Aura: Aura.f (Facade)
A very lightweight implementation of the facade pattern that provides modules
with limited access to the core. They must communicate through the facade.
aura.f = {
define:function( core, module, config ){
var core = aura.core, dom = core.dom, events =core.events,
component = core.dom.query(module)._elements;
return {
publish:function( e ){
events.trigger(e);
},
subscribe:function( e ){
events.register(e, module);
},
query:function( selector ){
return component.query(selector)._elements;
},
bind:function( el , type, fn ){
dom.bind(el, type, fn);
},
unbind:function( el , type, fn ){
dom.unbind(el, type, fn);
},
Thursday, 6 October 2011
73. Aura: Modules > add-todo.js
Module wireframe.
aura.core.define("#todo-list", function (f) {
var todos;
return {
init : function () {
/*..*/
},
destroy : function () { Let’s focus on this.
/*..*/
},
addEntry : function (todo) {
/*..*/
}
};
});
Thursday, 6 October 2011
74. Aura: Modules > add-todo.js
Module for adding new Todo items to a container on a page
init : function () {
todos = f.query("ul");
f.subscribe({
'add-todo' : this.addEntry
});
},
destroy : function () {
f.destroy(todos);
},
addEntry : function (todo) {
var entry = f.create("li", { 'class' : 'todo_entry', [
f.create("span", { 'class' :'todo_text', text : todo.value }),
f.create("span", { 'class' :'todo_date', text : todo.date })
]});
todos.append(entry);
f.publish({
type : 'added-todo',
data : todo.value
});
}
Thursday, 6 October 2011
75. Aura: Modules > add-todo.js
Note how modules are only concerned with publishing and subscripting to noti cations from the rest of the
application. They don’t touch other modules directly.
init : function () {
todos = f.query("ul"); 1. Cache the <ul> for a component representing the todo list (eg. a div)
f.subscribe({ 2. Subscribe to noti cations about a new todo item being available. (eg. a
'add-todo' : this.addEntry just clicked ‘add’ somewhere in the app)
user has
}); 3. A new noti cation will trigger our .addEntry() method
},
destroy : function () {
f.destroy(todos);
},
addEntry : function (todo) {
var entry = f.create("li", { 'class' : 'todo_entry', [
f.create("span", { 'class' :'todo_text', text : todo.value }),
f.create("span", { 'class' :'todo_date', text : todo.date })
]});
todos.append(entry);
f.publish({
type : 'added-todo',
data : todo.value
});
}
Thursday, 6 October 2011
76. Aura: Modules > add-todo.js
The facade allows access to everything from DOM query methods to dynamic object creation. It can be as
light or as heavy on feature-access as you wish.
init : function () {
todos = f.query("ul");
f.subscribe({
'add-todo' : this.addEntry
});
},
destroy : function () {
f.destroy(todos); 1. Dynamically create a new todo entry (a list element with child spans
}, for the Todo text and creation date)
2. Append this to the existing todo list cached earlier
addEntry : function (todo) {
var entry = f.create("li", { 'class' : 'todo_entry', [
f.create("span", { 'class' :'todo_text', text : todo.value }),
f.create("span", { 'class' :'todo_date', text : todo.date })
]});
todos.append(entry);
f.publish({
type : 'added-todo',
data : todo.value
});
}
Thursday, 6 October 2011
77. Aura: Modules > add-todo.js
Module for adding new Todo items to a container on a page
init : function () {
todos = f.query("ul");
f.subscribe({
'add-todo' : this.addEntry
});
},
destroy : function () {
f.destroy(todos);
},
addEntry : function (todo) {
var entry = f.create("li", { 'class' : 'todo_entry',children : [
f.create("span", { 'class' :'todo_text', text : todo.value }),
f.create("span", { 'class' :'todo_date', text : todo.date })
]});
todos.append(entry);
f.publish({
Publish a noti cation letting other modules know a todo
type : 'added-todo',
data : todo.value item has been added. The application can then react
}); accordingly to this action.
}
Thursday, 6 October 2011
78. Aura: Modules > counter.js
And here’s a basic module that consumes the noti cations from the last one.
aura.core.define("todo-counter", function(f){
var count, counter;
counter = f.query(".counter-text")
return{
init : function(){
f.subscribe({
'added-todo' : this.updateCount; Consuming events
});
},
destroy: function(){
f.destroy(count);
},
updateCount: function(e){
count++;
counter.html(count); Using library features
f.publish({
type: 'counter-updated', without directly touching them
data: count or the application core.
});
}
}
});
Thursday, 6 October 2011
79. What We Learned
Let’s review what we looked at today.
‘Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps
learning stays young. The greatest thing in life is to keep your mind young’
- Henry Ford
Thursday, 6 October 2011
80. Summary
Today we learned how to use three design patterns to create scalable ‘future-
proof’ application architectures. The idea is to have:
Application core Facade Modules
Mediator Core abstraction Highly decoupled
Module manager Permission manager Unique blocks
Swappable Framework agnostic
Thursday, 6 October 2011
81. That’s it.
For more on this architecture and other topics, check out:
Blog Twitter GitHub
http://addyosmani.com @addyosmani /addyosmani
Thursday, 6 October 2011