Slide 1

Slide 1 text

of large-scale JavaScript applications patters

Slide 2

Slide 2 text

Kim Joar Bekkelund I work here @kimjoar <- Twitter @kjbekkelund <- GitHub kimjoar.net

Slide 3

Slide 3 text

We’re going to talk about large JavaScript apps

Slide 4

Slide 4 text

We’re going to talk about large JavaScript apps where “large” really doesn’t need to be that large

Slide 5

Slide 5 text

Most of this will apply to the JavaScript apps we write

Slide 6

Slide 6 text

$('#my-button').click(function() { $.get('https://api.github.com', function(data) { $('#res').html(data.emojis_url); }); }); we always start with a small feature … The problem with JavaScript is that

Slide 7

Slide 7 text

suddenly 5k lines in one file “how did this happen?”

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Unobtrusive JavaScript cirka 2006

Slide 10

Slide 10 text

Unobtrusive JavaScript + cirka 2006

Slide 11

Slide 11 text

$

Slide 12

Slide 12 text

$ DOM MANIPULATION

Slide 13

Slide 13 text

$ $.fn.everything $(“.test”).parent().parent(); DOM MANIPULATION $(document).ready(everything)

Slide 14

Slide 14 text

$ DOM MANIPULATION Too often, the business logic is hidden in a mess of code

Slide 15

Slide 15 text

jQuery(document).ready(function() { $(".user form").submit(function(e) { e.preventDefault(); $.ajax({ // ... success: function(data) { var name = data.name || "Unknown"; $(".user .info").append("

" + name + "

"); } }) }); });

Slide 16

Slide 16 text

jQuery(document).ready(function() { $(".user form").submit(function(e) { e.preventDefault(); $.ajax({ // ... success: function(data) { var name = data.name || "Unknown"; $(".user .info").append("

" + name + "

"); } }) }); }); Page event User event Network I/O Network event Parsing response Templating Callback hell https://speakerdeck.com/bkeepers/the-plight-of-pinocchio

Slide 17

Slide 17 text

Considerable increase in the amount of JavaScript

Slide 18

Slide 18 text

Complex Single page apps now

Slide 19

Slide 19 text

backend --> frontend

Slide 20

Slide 20 text

New capabilities: geolocation, local storage, canvas, web workers, WebSockets backend --> frontend

Slide 21

Slide 21 text

The biggest problem is no longer the browser it’s the size and complexity of our apps

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

We want testable, scalable, maintainable JavaScript

Slide 24

Slide 24 text

This is difficult! actually

Slide 25

Slide 25 text

we need to unlearn

Slide 26

Slide 26 text

we need to unlearn our DOM-centric approach

Slide 27

Slide 27 text

we need to unlearn our DOM-centric approach The DOM is Inherently global and stateful hard to test and maintain

Slide 28

Slide 28 text

we need to unlearn how we write JavaScript

Slide 29

Slide 29 text

we need to unlearn how we write JavaScript No more 5k lines of code in a single file Watch out for global variables Learn from the backend

Slide 30

Slide 30 text

we need to unlearn how we use jQuery

Slide 31

Slide 31 text

we need to unlearn how we use jQuery “Our experience is that, when code is difficult to test, the most likely cause is that our design needs improving.” – Growing Object-Oriented Software, Guided by Tests

Slide 32

Slide 32 text

– Justin Meyer “The secret to building large apps is never build large apps. Break up your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application.”

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

There’s so much choice that you end up feeling like this guy

Slide 35

Slide 35 text

There’s so much choice that you end up feeling like this guy We need a foundation

Slide 36

Slide 36 text

we need to move state away from the DOM most importantly

Slide 37

Slide 37 text

MVC

Slide 38

Slide 38 text

MVC Google Trends for “JavaScript mvc”

Slide 39

Slide 39 text

MVC A clean separation between data and the DOM

Slide 40

Slide 40 text

MVC Mediates inputs and manipulates the model

Slide 41

Slide 41 text

MVC NOT THE ONLY ONE. MANY VARIATIONS.

Slide 42

Slide 42 text

MV*

Slide 43

Slide 43 text

MV* everyone has a different take

Slide 44

Slide 44 text

MVP all presentation logic is pushed to the presenter

Slide 45

Slide 45 text

MVVM exposes model so it can be easily managed and consumed

Slide 46

Slide 46 text

“I hereby declare AngularJS to be MVW framework – Model-View-Whatever. Where Whatever stands for ‘whatever works for you’.” – Igor Minar

Slide 47

Slide 47 text

The most important aspect is to separate STATE and the DOM

Slide 48

Slide 48 text

First step ✓

Slide 49

Slide 49 text

However, MV* is not enough

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

“Lots of monolithic single files that contained too much code.”

Slide 52

Slide 52 text

“Actually how you partition your code has a lot to do with how you organize your project source code and how much easier it is to maintain as your project grows.” – Jim Lavin

Slide 53

Slide 53 text

# files ++ filesize --

Slide 54

Slide 54 text

Package by feature Package by layer vs

Slide 55

Slide 55 text

Package by feature Package by layer vs

Slide 56

Slide 56 text

Package by feature I prefer

Slide 57

Slide 57 text

Package by feature I prefer I call them Modules

Slide 58

Slide 58 text

Module

Slide 59

Slide 59 text

Module

Slide 60

Slide 60 text

Module

Slide 61

Slide 61 text

Modules

Slide 62

Slide 62 text

Modulescontain business logic

Slide 63

Slide 63 text

Modules small are

Slide 64

Slide 64 text

Modules focused are

Slide 65

Slide 65 text

Modulesare decoupled from other modules

Slide 66

Slide 66 text

Modulesare preferably reusable

Slide 67

Slide 67 text

Modulesare easily testable

Slide 68

Slide 68 text

“A system is easier to change if its objects are context-independent; that is, if each object has no built-in knowledge about the system in which it executes.” – Growing Object-Oriented Software, Guided by Tests

Slide 69

Slide 69 text

Module rules http://www.slideshare.net/nzakas/scalable-javascript-application-architecture-2012

Slide 70

Slide 70 text

Module rules Never access the DOM outside the module http://www.slideshare.net/nzakas/scalable-javascript-application-architecture-2012

Slide 71

Slide 71 text

Module rules Never access the DOM outside the module Don’t create global variables http://www.slideshare.net/nzakas/scalable-javascript-application-architecture-2012

Slide 72

Slide 72 text

Module rules Never access the DOM outside the module Don’t create global variables Don’t access global, non-native objects http://www.slideshare.net/nzakas/scalable-javascript-application-architecture-2012

Slide 73

Slide 73 text

Keeping this in mind should get you a long way

Slide 74

Slide 74 text

Next up: dependencies

Slide 75

Slide 75 text

We depend on other code dependencies We depend on other files We depend on Third-party libraries

Slide 76

Slide 76 text

We depend on other code

Slide 77

Slide 77 text

We usually depend far too much on globals We depend on other code

Slide 78

Slide 78 text

We usually depend far too much on globals JavaScript makes this really easy We depend on other code

Slide 79

Slide 79 text

$('#my-button').click(function() { $.get('https://api.github.com', function(data) { $('#res').html(data.emojis_url); }); });

Slide 80

Slide 80 text

$('#my-button').click(function() { $.get('https://api.github.com', function(data) { $('#res').html(data.emojis_url); }); }); Cannot be reused Globals DIFFICULT TO TEST

Slide 81

Slide 81 text

var downloadEmojis = function() { $.get('https://api.github.com', function(data) { $('#res').html(data.emojis_url); }); }; $('#my-button').click(downloadEmojis);

Slide 82

Slide 82 text

var downloadEmojis = function() { $.get('https://api.github.com', function(data) { $('#res').html(data.emojis_url); }); }; $('#my-button').click(downloadEmojis); Globals DIFFICULT TO TEST

Slide 83

Slide 83 text

var downloadEmojis = function(ajax, $el) { ajax.get('https://api.github.com', function(data) { $el.html(data.emojis_url); }); }; $('#my-button').click(function() { downloadEmojis($, $('#res')); });

Slide 84

Slide 84 text

var downloadEmojis = function(ajax, $el) { ajax.get('https://api.github.com', function(data) { $el.html(data.emojis_url); }); }; $('#my-button').click(function() { downloadEmojis($, $('#res')); }); Now we can control the dependencies

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

var UserView = function(el, user) { this.el = el; this.user = user; }; // let's just create a super simple user object var user = { image: 'http://example.com/image.png' }; // initialize with the element the view owns and a user var view = new UserView($('.user'), user);

Slide 87

Slide 87 text

var UserView = function(el, user) { this.el = el; this.user = user; }; UserView.prototype.showImage = function() { this.el.append(''); }; // let's just create a super simple user object var user = { image: 'http://example.com/image.png' }; // initialize with the element the view owns and a user var view = new UserView($('.user'), user);

Slide 88

Slide 88 text

var UserView = function(el, user) { this.el = el; this.user = user; }; UserView.prototype.showImage = function() { this.el.append(''); }; // let's just create a super simple user object var user = { image: 'http://example.com/image.png' }; // initialize with the element the view owns and a user var view = new UserView($('.user'), user); // ... and now we can do stuff which changes the DOM view.showImage();

Slide 89

Slide 89 text

Always inject dependencies

Slide 90

Slide 90 text

has embraced this

Slide 91

Slide 91 text

var MyController = function($scope, $http) { $http.get('https://api.github.com') .success(function(data) { $scope.emojis_url = data.emojis_url; }); } An example using AngularJS

Slide 92

Slide 92 text

var MyController = function($scope, $http) { $http.get('https://api.github.com') .success(function(data) { $scope.emojis_url = data.emojis_url; }); } the ordering of these does not matter An example using AngularJS

Slide 93

Slide 93 text

var MyController = function($http, $scope) { $http.get('https://api.github.com') .success(function(data) { $scope.emojis_url = data.emojis_url; }); } the ordering of these does not matter An example using AngularJS

Slide 94

Slide 94 text

// Create an AngularJS module with no dependencies var ndc = angular.module('ndc', []);

Slide 95

Slide 95 text

// Create an AngularJS module with no dependencies var ndc = angular.module('ndc', []); // Register a function ndc.factory('awesome', function() { return 'awesome conference'; });

Slide 96

Slide 96 text

// Create an AngularJS module with no dependencies var ndc = angular.module('ndc', []); // Register a function ndc.factory('awesome', function() { return 'awesome conference'; }); // Get the injector (happens behind the scenes in AngularJS apps) var injector = angular.injector(['ndc', 'ng']);

Slide 97

Slide 97 text

// Create an AngularJS module with no dependencies var ndc = angular.module('ndc', []); // Register a function ndc.factory('awesome', function() { return 'awesome conference'; }); // Get the injector (happens behind the scenes in AngularJS apps) var injector = angular.injector(['ndc', 'ng']);

Slide 98

Slide 98 text

// Create an AngularJS module with no dependencies var ndc = angular.module('ndc', []); // Register a function ndc.factory('awesome', function() { return 'awesome conference'; }); // Get the injector (happens behind the scenes in AngularJS apps) var injector = angular.injector(['ndc', 'ng']); // Call a function with dependency injection injector.invoke(function(awesome) { console.log('awesome() == ' + awesome); });

Slide 99

Slide 99 text

// Create an AngularJS module with no dependencies var ndc = angular.module('ndc', []); // Register a function ndc.factory('awesome', function() { return 'awesome conference'; }); // Get the injector (happens behind the scenes in AngularJS apps) var injector = angular.injector(['ndc', 'ng']); // Call a function with dependency injection injector.invoke(function(awesome) { console.log('awesome() == ' + awesome); });

Slide 100

Slide 100 text

http://docs.angularjs.org/guide/dev_guide.templates.databinding

Slide 101

Slide 101 text

http://docs.angularjs.org/guide/dev_guide.templates.databinding

Slide 102

Slide 102 text

{{#if isExpanded}}
{{body}}
Contract {{else}} Show More... {{/if}} App.PostController = Ember.ObjectController.extend({ body: 'body', isExpanded: false, expand: function() { this.set('isExpanded', true); }, contract: function() { this.set('isExpanded', false); } });

Slide 103

Slide 103 text

{{#if isExpanded}}
{{body}}
Contract {{else}} Show More... {{/if}} App.PostController = Ember.ObjectController.extend({ body: 'body', isExpanded: false, expand: function() { this.set('isExpanded', true); }, contract: function() { this.set('isExpanded', false); } }); no jquery!

Slide 104

Slide 104 text

dependencies We depend on other code We depend on other files We depend on Third-party libraries

Slide 105

Slide 105 text

We depend on other files

Slide 106

Slide 106 text

The order of elements

Slide 107

Slide 107 text

The order of elements

Slide 108

Slide 108 text

The order of elements Lots of globals Weakly stated dependencies

Slide 109

Slide 109 text

CommonJS AMD

Slide 110

Slide 110 text

CommonJS AMD var $ = require('jquery'); exports.myFn = function () {};

Slide 111

Slide 111 text

CommonJS AMD var $ = require('jquery'); exports.myFn = function () {}; define(['jquery'] , function ($) { return function () {}; });

Slide 112

Slide 112 text

CommonJS AMD var $ = require('jquery'); exports.myFn = function () {}; define(['jquery'] , function ($) { return function () {}; }); works better in current browsers

Slide 113

Slide 113 text

AMD

Slide 114

Slide 114 text

dependencies We depend on other code We depend on other files We depend on Third-party libraries

Slide 115

Slide 115 text

We depend on Third-party libraries

Slide 116

Slide 116 text

Package manager

Slide 117

Slide 117 text

Bower // bower.json { "dependencies": { "angular": "~1.0.7", "angular-resource": "~1.0.7", "jquery": "1.9.1" } }

Slide 118

Slide 118 text

Bower // bower.json { "dependencies": { "angular": "~1.0.7", "angular-resource": "~1.0.7", "jquery": "1.9.1" } } $ bower install

Slide 119

Slide 119 text

No content

Slide 120

Slide 120 text

… and that’s as far as we’ll go today

Slide 121

Slide 121 text

is all of this necessary? … but seriously,

Slide 122

Slide 122 text

Want to learn more?

Slide 123

Slide 123 text

No content

Slide 124

Slide 124 text

Questions?