Slide 1

Slide 1 text

LARGE-SCALE USING JAVASCRIPT AND JQUERY BUILDING WITH @ADDYOSMANI APPLICATIONS #JQCON #LARGESCALE

Slide 2

Slide 2 text

I WORK AT A COMPANY CALLED

Slide 3

Slide 3 text

BUILDING JAVASCRIPT APPS THAT HAVE TO SCALE FROM THE GET-GO

Slide 4

Slide 4 text

WHAT DOES THAT INVOLVE?

Slide 5

Slide 5 text

DECOUPLING BASIC CONCEPTS

Slide 6

Slide 6 text

SEPARATES UNITS OF CODE THAT DON’T DEPEND ON EACH OTHER

Slide 7

Slide 7 text

FILTER FORMULA EDITOR GRID SPREADSHEET WIDGET/MODULE

Slide 8

Slide 8 text

PROMOTES REUSABILITY LESS COUPLING = EASIER REUSE

Slide 9

Slide 9 text

BUT THAT’S NOT ALL! DECOUPLING CAN BE TAKEN FURTHER

Slide 10

Slide 10 text

REDUCING THE RISK OF BREAKAGE WHEN OTHER MODULES FAIL EXAMPLE

Slide 11

Slide 11 text

COUPLED, DIRECT CALLS //Coupled $.when( $.ajax("mail.php") ) .then(function( ajaxArgs ){ var jqXHR = ajaxArgs[2], data = jqXHR.responseText; // Display notification and preview modals.show('New Message', data); // What happens if something breaks here? // Play new message sound audioManager.play('newMessage'); // Update the unread message count messages.increaseMessageCount('unread'); });

Slide 12

Slide 12 text

DECOUPLED, EVENT-DRIVEN // Decoupled, Event-driven $.when( $.ajax("mail.php") ) .then(function( ajaxArgs ){ var jqXHR = ajaxArgs[2], data = jqXHR.responseText; // Tell the application there is a new message, share the data $.publish('newMessage', data); // Other modules can now just listen out for 'newMessage' }); $.subscribe('newMessage', ...); // modals.show() // How about now? $.subscribe('newMessage', ...); // audioManager.play() $.subscribe('newMessage', ...); // messages.increaseMessageCount()

Slide 13

Slide 13 text

PUB/SUB BASIC CONCEPTS

Slide 14

Slide 14 text

LOGICALLY DECOUPLE OBJECTS GENERATING EVENTS FROM THOSE REACTING TO THEM NEW MESSAGE ‘HELLO WORLD’ THERE’S A NEW MESSAGE!

Slide 15

Slide 15 text

BAKED INTO MVC, MV* MODELS VIEWS CONTROLLERS MODELS CAN BE OBSERVED FOR CHANGES

Slide 16

Slide 16 text

OPTION 1: JQUERY CUSTOM EVENTS $(document).trigger('eventName'); //equivalent to $.publish('eventName') $(document).on('eventName',...); //equivalent to $.subscribe('eventName',...) // Example: $(document).on('newMessage', function(){ displayNewMailNotification(); }); $(document).trigger('newMessage');

Slide 17

Slide 17 text

OPTION 2: PUB/SUB WRAPPER // Using .on()/.off() from jQuery 1.7.1 (function($) { var o = $({}); $.subscribe = function() { o.on.apply(o, arguments); }; $.unsubscribe = function() { o.off.apply(o, arguments); }; $.publish = function() { o.trigger.apply(o, arguments); }; }(jQuery));

Slide 18

Slide 18 text

OPTION 3: $.CALLBACKS // Multi-purpose callbacks list object // Pub/Sub implementation: var topics = {}; jQuery.Topic = function( id ) { var callbacks, topic = id && topics[ id ]; if ( !topic ) { callbacks = jQuery.Callbacks(); topic = { publish: callbacks.fire, subscribe: callbacks.add, unsubscribe: callbacks.remove }; if ( id ) { topics[ id ] = topic; } } return topic; };

Slide 19

Slide 19 text

OPTION 3: $.CALLBACKS // Usage: // Subscribers $.Topic( 'mailArrived' ).subscribe( fn1 ); $.Topic( 'mailArrived' ).subscribe( fn2 ); $.Topic( 'mailSent' ).subscribe( fn1 ); // Publishers: $.Topic( 'mailArrived' ).publish( 'hello world!' ); $.Topic( 'mailSent' ).publish( 'woo! mail!' );

Slide 20

Slide 20 text

OPTION 4: BACKBONE.JS EVENTS var myObject = {}; _.extend( myObject, Backbone.Events ); myObject.on('eventName', function( msg ) { console.log( 'triggered:' + msg ); }); myObject.trigger('eventName', 'some event'); // Similarly.. person.on('change:name change:age', function( msg ) { console.log('name and age changed'); });

Slide 21

Slide 21 text

CHECK-UP

Slide 22

Slide 22 text

YOUR CURRENT ARCHITECTURE MODULES WIDGETS MVC LIBRARIES TOOLKITS APPLICATION CORE TEMPLATES

Slide 23

Slide 23 text

IF ONE PART OF YOUR APPLICATION BREAKS CAN THE APP FIX THIS BREAK ITSELF? BUT THERE ARE PROBLEMS:

Slide 24

Slide 24 text

HOW MUCH OF WHAT WE CREATE IS EASILY REUSABLE? HOW SECURE IS YOUR APPLICATION FROM ITSELF? CAN OUR MODULES BE TESTED INDEPENDENTLY? MORE PROBLEMS

Slide 25

Slide 25 text

THINK LONG TERM! WHAT HAVEN’T YOU FACTORED IN YET? WHAT MIGHT CHANGE?

Slide 26

Slide 26 text

“THE SECRET TO BUILDING LARGE APPS IS NEVER BUILD LARGE APPS. BREAK YOUR APP INTO SMALL PIECES. THEN, ASSEMBLE THOSE TESTABLE, BITE-SIZED PIECES INTO YOUR BIG APPLICATION” - JUSTIN MEYER

Slide 27

Slide 27 text

PATTERNS

Slide 28

Slide 28 text

FACADE PATTERN

Slide 29

Slide 29 text

SIMPLIFIES USAGE OF A MODULE THROUGH A LIMITED API

Slide 30

Slide 30 text

$( el ).css() $( el ).animate(..) $( el ).attributes( key, value ) ... EXAMPLES YOU USE ALL THE TIME

Slide 31

Slide 31 text

HIDES IMPLEMENTATION LEVEL DETAILS

Slide 32

Slide 32 text

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); _private.get(); if ( args.run ) { _private.run(); } } } }()); module.facade({run: true, val:10}); //outputs current value: 10, running A limited API allowing access to a more complex implementation FACADE IMPLEMENTATION

Slide 33

Slide 33 text

return { facade : function( args ) { // set values of private properties _private.set(args.val); // test setter _private.get(); // optional: provide a simple interface // to internal methods through the // facade signature if ( args.run ) { _private.run(); } } } module.facade({run: true, val:10}); Users are only concerned with using the facade FACADE IMPLEMENTATION

Slide 34

Slide 34 text

ENABLES COMPLEXITY BEHIND THE SCENES

Slide 35

Slide 35 text

bindReady: function() { ... // One snippet for event listeners in jQuery core where the user // isn't exposed to any of the cross-browser implementation // handling behind the scenes if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded",DOMContentLoaded, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); // If IE event model is used } else if ( document.attachEvent ) { document.attachEvent( "onreadystatechange", DOMContentLoaded); and another .bindReady() used for $(document).ready()

Slide 36

Slide 36 text

MEDIATOR PATTERN

Slide 37

Slide 37 text

TOWER HANDLES WHAT PLACES CAN TAKE OFF OR LAND ALL COMMUNICATION DONE FROM PLANES TO TOWER, NOT PLANE TO PLANE A CENTRALISED CONTROLLER IS KEY TO SUCCESS HERE ANALOGY: AIR TRAFFIC CONTROL

Slide 38

Slide 38 text

ALLOWS MODULES TO BROADCAST OR LISTEN FOR NOTIFICATIONS A MEDIATOR:

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

// Publish-Subscribe via a centralised mediator mediator.subscribe('messageReceived', function( data ){ // Awesome! a new message // I can do something with the data }); mediator.publish('messageReceived', { sender: '[email protected]', preview: 'hello world' }); MEDIATOR EXAMPLE:

Slide 41

Slide 41 text

NOTIFICATIONS CAN BE HANDLED BY ANY NUMBER OF MODULES CENTRALISED PUB/SUB

Slide 42

Slide 42 text

MESSAGES CHAT NOTIFICATIONS A new message is available A new notification has been posted Any new emails? Paul sent a new IM Any new instant messages? Are any contacts online to chat? 5 contacts are available to chat

Slide 43

Slide 43 text

AS CALLS PASS-THROUGH, IT CAN PERFORM VALIDATION AND MUCH MORE MORE THAN JUST PUBLISH/SUBSCRIBE

Slide 44

Slide 44 text

MODULES

Slide 45

Slide 45 text

MODULE PATTERN OBJECT LITERALS AMD ASYNCHRONOUS MODULES - UNTIL WE GET ES HARMONY SIMULATED PRIVACY MODULAR OBJECTS

Slide 46

Slide 46 text

LARGE APPLICATIONS

Slide 47

Slide 47 text

WHAT IS A LARGE APP? A NON-TRIVIAL APPLICATION REQUIRING SIGNIFICANT DEVELOPER EFFORT TO MAINTAIN, WHERE MOST OF THE HEAVY LIFTING OF DATA MANIPULATION AND DISPLAY FALLS TO THE BROWSER IT ISN’T ABOUT LOC!

Slide 48

Slide 48 text

GWT->JAVASCRIPT, LOTS OF COMPONENTS GMail

Slide 49

Slide 49 text

MODULARIZED HOMEPAGE Yahoo!

Slide 50

Slide 50 text

BRAINSTORM

Slide 51

Slide 51 text

LOOSELY COUPLED ARCHITECTURE SMALLER INDEPENDENT MODULES FLEXIBILITY TO CHANGE WHAT ARE WE LOOKING FOR?

Slide 52

Slide 52 text

NOTIFY US WHEN SOMETHING INTERESTING HAPPENS DON’T TOUCH ANYTHING THEY DON’T HAVE TO CAN’T CAUSE THE ENTIRE APP TO STOP WORKING MODULES:

Slide 53

Slide 53 text

DEDICATE SUFFICIENT TIME TO PLANNING THE ARCHITECTURE THE MAKES THE MOST SENSE FOR YOUR APP - IT’S OFTEN MORE COMPLEX THAN WE INITIALLY THINK.

Slide 54

Slide 54 text

SOLUTION: COMBO! + +

Slide 55

Slide 55 text

1. THE APPLICATION CORE USING THE MEDIATOR PATTERN MANAGES THE MODULE LIFECYCLE: STARTS, STOPS, RESTARTS IF NECESSARY

Slide 56

Slide 56 text

CODE SAMPLE: app.core = (function(){ var data = {}; return{ define: function( id, constructor ){ //... type checking not show var c = constructor(app.facade.define(this,id)); //..further instance checks not shown data[id] = { define: constructor, instance:null; }, start: function( id ){ var module = data[id]; module.instance =module.define(app.facade.define(this, id)); module.instance.initialize(); }, stop: function( id ){ var module = data[id]; if(module.instance){ module.instance.destroy(); } ...

Slide 57

Slide 57 text

1. THE APPLICATION CORE USING THE MEDIATOR PATTERN REACTS TO ACTIONS PASSED BACK FROM A SANDBOX (FACADE) - HANDLES LOGIC

Slide 58

Slide 58 text

CODE SAMPLE: // The sandbox has publish() and subscribe() methods // which the mediator implements // subscribe register:function(events, module){ if(app.core.dom.isObject(events) && module){ if(data[module]){ data[module].events = events; } } }, // publish trigger: function(events){ if(app.core.dom.isObject(events)){ var mod; for(mod in data){ if(data.hasOwnProperty(mod)){ mod = data[mod]; if(mod.events && mod.events[events.type]){ ...

Slide 59

Slide 59 text

1. THE APPLICATION CORE USING THE MEDIATOR PATTERN ENABLES ADDING AND REMOVING MODULES WITHOUT CAUSING BREAKS

Slide 60

Slide 60 text

CODE SAMPLE: // Summary definitions of a simple todo app // Easy to add/remove/start/stop app.core.define('#todo-counter', ...); app.core.define('#todo-field',...); app.core.define('#validate', ...); app.core.define("#todo-list", ...); app.core.define("#todo-entry", ...); app.core.define('#error', ...); app.core.define('#validate', ...);

Slide 61

Slide 61 text

1. THE APPLICATION CORE USING THE MEDIATOR PATTERN HANDLES ERROR DETECTION AND MANAGEMENT

Slide 62

Slide 62 text

2. THE SANDBOX USING THE FACADE PATTERN AN ABSTRACTION OF THE CORE THAT’S AN API FOR COMMON TASKS, USED BY MODULES

Slide 63

Slide 63 text

CODE SAMPLE: app.sandbox = { define: function( el, module ){ return{ publish: function( eventType ){ app.events.trigger( eventType ); }, subscribe: function( eventType ){ app.events.register( eventType, module); }, find: function( selector ){ return app.dom.query(el).find(selector); }, ignore: function( eventType ){ app.events.remove( eventType, module); } } } };

Slide 64

Slide 64 text

2. THE SANDBOX USING THE FACADE PATTERN INTERFACE FOR ENSURING MODULES DON’T DIRECTLY ACCESS THE CORE/LIBRARIES

Slide 65

Slide 65 text

CODE SAMPLE: // Note: all input validation done by the mediator animate:function( props ){ return core.dom.animate(props); }, bind:function( el , type, fn ){ dom.bind(el, type, fn); }, unbind:function( el , type, fn ){ dom.unbind(el, type, fn); }, ignore:function( e ){ events.remove(e, module); }, createElement:function( el, config ){

Slide 66

Slide 66 text

2. THE SANDBOX USING THE FACADE PATTERN PERMISSIONS MANAGER, SECURING WHAT MODULES CAN/CAN’T ACCESS

Slide 67

Slide 67 text

CODE SAMPLE: // A permissions structure can support checking // against subscriptions prior to allowing them // to clear. This enforces a flexible security // layer for your application. var permissions = { newMessage: { messageComposer:true, notifications: true, offlineStore: false }, trashMessage:{ messageComposer: true, notifications: false }, ...

Slide 68

Slide 68 text

3. MODULES: UNIQUE (INDEPENDENT) BLOCKS OF FUNCTIONALITY FOR YOUR APPLICATION USING AMD/OBJECT LITERALS/MODULE PATTERN

Slide 69

Slide 69 text

CODE SAMPLE (STATUS): app.core.define('#status-widget', function( sandbox ){ var status = "none"; return{ initialize: function(){ var el = sandbox.find('.editor'); sandbox.subscribe({ 'new-entry': this.updateStatus }); }, destroy: function(){ status = ""; }, updateStatus: function(data){ status = "edited"; sandbox.find('.status').html(status); sandbox.publish('status-updated'); } } });

Slide 70

Slide 70 text

3. MODULES: SUBSCRIBE TO NOTIFICATIONS OF INTEREST, RAPIDLY REACT TO CHANGES USING AMD/OBJECT LITERALS/MODULE PATTERN

Slide 71

Slide 71 text

CODE SAMPLE: app.core.define('#status-widget', function( sandbox ){ var status = "none"; return{ initialize: function(){ var el = sandbox.find('.editor'); sandbox.subscribe({ 'new-entry': this.updateStatus }); }, destroy: function(){ status = ""; }, updateStatus: function(data){ status = "edited"; sandbox.find('.status').html(status); sandbox.publish('status-updated'); } } });

Slide 72

Slide 72 text

3. MODULES: NOTIFY THE APP WHEN SOMETHING INTERESTING HAPPENS (PUBLISH) USING AMD/OBJECT LITERALS/MODULE PATTERN

Slide 73

Slide 73 text

CODE SAMPLE: app.core.define('#status-widget', function( sandbox ){ var status = "none"; return{ initialize: function(){ var el = sandbox.find('.editor'); sandbox.subscribe({ 'new-entry': this.updateStatus }); }, destroy: function(){ status = ""; }, updateStatus: function(data){ .. sandbox.publish({ type : 'status-updated', data : {value: ‘A new entry has been added’} }); } }

Slide 74

Slide 74 text

DEMO TIME! SEE: https://github.com/addyosmani/largescale-demo

Slide 75

Slide 75 text

REVIEW

Slide 76

Slide 76 text

A PATTERN FOR LARGE-SCALE APPS CORE SANDBOX MODULES

Slide 77

Slide 77 text

• Modules have fewer dependencies • They can be managed so that the application doesn’t (or shouldn’t) fall over. • You can theoretically pick up any module, drop it in a page and start using it in another project AND ONE LAST REMINDER ABOUT ‘WHY?’

Slide 78

Slide 78 text

FURTHER READING

Slide 79

Slide 79 text

• Patterns For Large-scale JavaScript Application Architecture http://addyosmani.com/largescalejavascript/ • Understanding The Publish/Subscribe Pattern http:// msdn.microsoft.com/en-us/scriptjunkie/hh201955.aspx • Writing Modular JavaScript with AMD, CommonJS & ES Harmony http://addyosmani.com/writing-modular-js • Understanding MVC & MVP For JS Developers http:// bit.ly/wJRR59 MORE INFORMATION

Slide 80

Slide 80 text

• Backbone Fundamentals (book): http:// backbonefundamentals.com • Essential JavaScript Design Patterns (e-book) http:// bit.ly/bMyoQ9 • Building Large-Scale jQuery Applications http:// addyosmani.com/blog/large-scale-jquery/ FREE BOOKS/ARTICLES

Slide 81

Slide 81 text

Blog Twitter GitHub http://addyosmani.com @addyosmani or @addy_osmani http://github.com/addyosmani THAT’S IT! FOR MORE FROM ME: