43
Architecting Web Applications Build Modular Web Applications Using Backbone.js and RequireJS

Rp 6 session 2 naresh bhatia

Embed Size (px)

Citation preview

Page 1: Rp 6  session 2 naresh bhatia

Architecting Web Applications Build Modular Web Applications Using Backbone.js and RequireJS

Page 2: Rp 6  session 2 naresh bhatia

Naresh Bhatia

CTO, Sapient Global Markets Co-lead Visualization Practice

Experience Trading and Risk Management applications using JavaScript, Java and .NET

Founder Archfirst (http://archfirst.org) A place for software developers to learn and compare technologies through real world examples

Page 3: Rp 6  session 2 naresh bhatia

A case study in visualization

Page 4: Rp 6  session 2 naresh bhatia

A technologist’s side-project that was functional, but lacked finesse.

Page 5: Rp 6  session 2 naresh bhatia

Before

Page 6: Rp 6  session 2 naresh bhatia

In Between

Page 7: Rp 6  session 2 naresh bhatia

After

Page 8: Rp 6  session 2 naresh bhatia

What does it take to build such applications?

Page 9: Rp 6  session 2 naresh bhatia

JavaScript Space used to be The Wild West

Sliding banners

5000 lines of procedural code in a single file

Complex DOM spaghetti

Page 10: Rp 6  session 2 naresh bhatia

The landscape is changing now

Page 11: Rp 6  session 2 naresh bhatia

Recent Advances in JavaScript

• AJAX enables changing of content without refreshing the entire page

• Browsers implement better and faster JavaScript engines

• Rise of smart web “applications” (vs. static web sites) – Gmail

– Facebook

– Twitter

• People start talking about “architecture” of their JavaScript applications – Object-orientation

– Design patterns

– MVC

– In-application messaging

– Modularity

Page 12: Rp 6  session 2 naresh bhatia

JavaScript is a respectable platform today

Serious JavaScript developers do worry

about architecture these days!

Page 13: Rp 6  session 2 naresh bhatia

Model-View-Controller (MVC) A pattern that encourages separation of concerns and code reuse

Page 14: Rp 6  session 2 naresh bhatia

MVC Philosophy

Page 15: Rp 6  session 2 naresh bhatia

Separate application state from its presentation

Separate models from their views

Get the truth out of the DOM

Page 16: Rp 6  session 2 naresh bhatia

MVC Philosophy – Get the truth out of the DOM

Doesn’t matter what flavor of MVC you are using

MVC, MVP, MVVM, MV*, MVWhatever

Models

Views

Page 17: Rp 6  session 2 naresh bhatia

Introducing Backbone – A Lightweight MV* Framework

• Organize your interface into logical views backed by models – Each view responsible for one DOM element

– Each view can be updated independently when the model changes

– Reduces DOM traversal, increasing performance

• See here for an example (slide 8)

• No need to dig into a JSON object, look up an element in the DOM, and update the HTML by hand – Bind your view's render function to the model's "change" event

– Now every view that displays this model is updated immediately on a change

Page 19: Rp 6  session 2 naresh bhatia

Backbone Models

• Models are Backbone objects that contain “attributes”

• Whenever an attribute’s value changes, the model triggers a change event

// Define a model var Tile = Backbone.Model.extend({ }); // Create an instance of the model var tile = new Tile( ); // Set an attribute to fire the change event tile.set({color: 0xF89F1B});

“change” event Tile

color: F89F1B

Page 20: Rp 6  session 2 naresh bhatia

Updating views from change events

tile

tileRectView

color: F89F1B

el: model:

// 1. Create an instance of the model var tile = new Tile( ); // 2. Create a view, attach it to the model & DOM var tileRectView = new TileRectView ({el: '#tile‘, model: tile}); // 3. (constructor) Bind view to model change event this.model.on('change', this.render); // 4. Change a model attribute tile.set({color: 0xF89F1B}); // 5. Model fires a ‘change’ event // 6. View catches the event & calls render( ) this.$el.css( 'background-color', '#' + color2hex(this.model.get('color')));

1

2

render( )

4

‘change’ event

6

DOM

<div id="tile" ></div>

5

3

Page 21: Rp 6  session 2 naresh bhatia

Collections

brokerageAccounts

accountTableView

el: collection:

this.$el.empty(); this.collection.each(function(account, i) { var view = new AccountView({model: account}); this.$el.append(view.render().el); }, this);

DOM

<table id="accounts_table"> <thead>...</thead> <tbody></tbody> </table>

initialize( ) render( )

this.collection.bind('reset', this.render);

brokerageAccount

brokerageAccount

brokerageAccount

Page 22: Rp 6  session 2 naresh bhatia

Composing an interface from multiple views – View Hierarchy

AccountsPage

AccountsTab

UserPageHeaderWidget

AccountTableWidget AccountChartWidget

AccountView

FooterWidget

AccountView

AccountView

AccountView

Page 23: Rp 6  session 2 naresh bhatia

Inserting pages and widgets dynamically

index.html

<!DOCTYPE html> <html> <head> ... </head> <body> <div id="container"> <!-- Pages go here --> </div> </body> </html>

Page 24: Rp 6  session 2 naresh bhatia

A typical page

AccountsPage.js

BaseView.extend({ postRender: function() { this.addChildren([ { viewClass: UserPageHeaderWidget, parentElement: this.$el }, { viewClass: AccountsTab, parentElement: this.$el }, { viewClass: FooterWidget, parentElement: this.$el } ]); } }

Page 25: Rp 6  session 2 naresh bhatia

A typical widget

UserPageHeaderWidget.js

BaseView.extend({ tagName: 'header', className: 'user-page-header', template: { name: 'UserPageHeaderTemplate', source: UserPageHeaderTemplate } });

UserPageHeaderTemplate.html

<div class="user-info"> <span>{{firstName}} {{lastName}}</span> <img src="img/person-icon-small.png" alt="seperator" /> <a class="js-signOut" href="#">Sign Out</a> </div> <h1 class="ir">Bullsfirst</h1>

Page 26: Rp 6  session 2 naresh bhatia

Classic Server-Side Layered Architecture

User Interface Layer

Accepts user commands and presents information back to the user

Application Layer

Manages transactions, translates DTOs, coordinates application activities, creates and accesses domain objects

Domain Layer

Contains the state and behavior of the domain

Infrastructure Layer

Supports all other layers, includes repositories, adapters, frameworks etc.

Page 27: Rp 6  session 2 naresh bhatia

Bullsfirst Client-Side Layered Architecture F

ram

ew

ork

P

rese

nta

tio

n L

aye

r D

om

ain

Page 28: Rp 6  session 2 naresh bhatia

In-Application Messaging

• Interactions between table and chart

• If theses widgets interacted directly, the coupling would be too tight

• Use a message bus (a.k.a. pub/sub) to decouple the components

Account : mouseover

Page 29: Rp 6  session 2 naresh bhatia

JavaScript Modularity A pattern that supports large scale application development

Page 30: Rp 6  session 2 naresh bhatia

How much of your application can you hold in your head at once?

Bullsfirst is about 2000 lines of JS

Page 31: Rp 6  session 2 naresh bhatia

Have you ever been in Dependency Hell?

<script src="js/form2js.js"></script>

<script src="js/toObject.js"></script>

<script src="js/base64_encode.js"></script>

<script src="js/utf8_encode.js"></script>

<script src="js/format.js"></script>

<script src="js/treeTable.js"></script>

<script src="js/jalerts.js"></script>

Page 32: Rp 6  session 2 naresh bhatia

AMD and RequireJS to the rescue

AMD: Asynchronous Module Definition (an API for defining and loading JS modules)

RequireJS: A popular implementation of AMD

Page 33: Rp 6  session 2 naresh bhatia

Modules allow you to break large applications into bite-size chunks

Code reuse & separation of concerns

Complements the MVC pattern

Page 34: Rp 6  session 2 naresh bhatia

Structure of a Modular App

src |-- js | |-- app | | |-- domain | | | |-- Repository.js | | | |-- BrokerageAccount.js | | | `-- ... | | |-- pages | | | |-- home | | | |-- accounts | | | `-- ... | | `-- widgets | | `-- account-table | | `-- account-chart | | `-- ... | |-- framework | | |-- Baseview.js | | |-- Router.js | | `-- ... | `-- vendor | |-- backbone.js | |-- jquery.js | `-- ... |-- sass |-- img `-- index.html

60+ JavaScript files 2000 lines of code But much easier to find things!

JavaScript and templates (markup)

JavaScript and templates (markup)

Page 35: Rp 6  session 2 naresh bhatia

Defining a Module

RequireJS loads all code relative to a baseUrl (set to the same directory as the script used in a data-main)

// app/widgets/login/LoginWidget // Defines a widget called LoginWidget define( [], function() { return BaseView.extend({ ... }); } );

Page 36: Rp 6  session 2 naresh bhatia

Using a Module

Defining dependency on a module makes sure that the module is pulled in before the dependent code.

// app/pages/home/HomePage // Defines the home page that uses the LoginWidget define( ['app/widgets/login/LoginWidget'], function(LoginWidget) { return BaseView.extend({ postRender: function() { this.addChild({ id: 'LoginWidget', viewClass: LoginWidget, parentElement: this.$el }); } ... }); } );

Page 37: Rp 6  session 2 naresh bhatia

Moving templates into text modules

<td class="name"> {{name}} </td> <td class="market-value"> {{formatMoney marketValue}} </td> <td class="cash"> {{formatMoney cashPosition}} </td> ...

AccountTemplate.html

Page 38: Rp 6  session 2 naresh bhatia

Using templates defined in text modules

define( ['text!app/widgets/account-table/AccountTemplate.html'], function(AccountTemplate) { return BaseView.extend({ template: { name: 'AccountTemplate', source: AccountTemplate }, ... }); } );

AccountView.js

Page 39: Rp 6  session 2 naresh bhatia

Optimizing for Production – The Build System

• A web application consisting of hundreds of JavaScript files could have severe performance implications in a production environment

• RequireJS comes with an optimizer that walks the dependency tree and concatenates all the required JavaScript files into one

• The Bullsfirst build system runs the RequireJS optimizer plus other optimizations such as yuicompressor and uglifyjs to reduce the download size of the application – The build system is based on Grunt.js, a task-based command line build tool for

JavaScript projects

– It cuts down application load time to approximately 2 seconds on a 3 Mbps network

Page 40: Rp 6  session 2 naresh bhatia

Summary

Page 41: Rp 6  session 2 naresh bhatia

Summary

• Use the MVC pattern to enforce separation of concerns

• Compose your application using smaller Reusable Components

• Create a Domain Layer as a central place for views to access information

• Use In-Application Messaging to decouple application components

• Use Modules to break large applications into bite-size chunks

A strong architecture is key to building engaging web applications

Page 42: Rp 6  session 2 naresh bhatia

References

Page 43: Rp 6  session 2 naresh bhatia

References

• Backbone.js – https://github.com/documentcloud/backbone

• RequireJS – https://github.com/jrburke/requirejs

• Bullsfirst is an open-source project under http://archfirst.org – Sharing best practices with the developer community

– Live demo at http://archfirst.org/apps/bullsfirst-jquery-backbone

– Source repository: https://github.com/archfirst/bullsfirst-jquery-backbone

– Discussion/feedback at http://archfirst.org/forums/general-discussion

43