Large JavaScript Application Architecture design Pattern _ Basics

Source: Internet
Author: User
Tags exception handling modifiers

PDF version of the PPT download address: http://www.slideshare.net/jibyjohnc/jqquerysummit-largescale-javascript-application-architecture

Note: In the process of finishing, found that some of the author's thoughts are back to say, so the deletion of a part, if your English is good, please read the English ppt directly.

The following are the main chapters of this article:

1. What is called "JavaScript large program"?

2. Look at the current program structure

3. Long-term consideration

4. Brainstorming

5. The proposed framework

5.1 Design mode

5.1.1 Module theory

A summary of 5.1.1.1

5.1.1.2 Module mode

5.1.1.3 Object Self-level

5.1.1.4 COMMONJS Module

5.1.2 Facade mode

5.1.3 Mediator Mode

5.2 Application to your architecture

5.2.1 Façade-Core abstraction

5.2.2 Mediator-Program Core

5.2.3 work closely together.

6. Publish pub/Subscription sub extensions: Autoenrollment events

7. Q & A

8. Thanks

What do you mean, "javascript large programs"?

Before we begin, let's define what is called a large JavaScript site, many experienced JS developers are also challenge live, some people say more than 100,000 lines of JavaScript code is large, Others say JavaScript code to more than 1MB size, in fact, 2 can not be counted on, because can not install the amount of code to measure, a lot of trivial JS code is easy to more than 100,000 lines.

My definition of "big" is as follows, although it may not be right, but it should be relatively close:

I personally think that large JavaScript programs should be very important and integrate with many of the great developer efforts to process heavyweight data and show it to browsers.

Review the current program architecture

I can't emphasize how important this problem is, as many experienced developers often say: "The existing ideas and design patterns work very well on my last midsize project, so it should be okay to use them again in a slightly larger program, right?" "It's true in some programs, but don't forget that since it's a large program, there's usually a big concerns that needs to be broken down, and I'm going to take the time to review the program architecture that's been running for so long. In most cases, the current JavaScript program architecture should look like this (note that the JS architecture, not what we often call asp.net MVC):
Custom Widgets
Models
Views
Controllers
Templates
Libraries/toolkits
An application core.

You may also be able to encapsulate the program individually into multiple modules, or use other design patterns, which is fine, but if these structures are entirely representative of your architecture, there may be potential problems, so let's take a look at a few important points:

1. How many things in your architecture can be immediately reused?
are there some separate module dependencies that are not dependent on other code? Is it self contained? If I go to the code base you're working on, pick up some modular module code and put it on a new page, can you use it immediately? You might say the principle is OK, I suggest that you plan for a long time, if your company has developed a lot of important programs, suddenly one day someone said, the project's chat module is good, we take it out in another project, you can directly bring over the code can not modify it?

2. How many module modules in the system need to rely on other modules?
are all modules of the system tightly coupled? Before I take this issue as a concern, let me explain, not that all modules have absolutely nothing to rely on, such as a fine-grained function may be extended from the base function, my problem is not the same as this situation, I am talking about the dependencies of different functional modules, theoretically, All the different functional modules should not have too much reliance.

3. If some part of your program goes wrong, can other parts still work?
If you build a similar program with Gmail, you can find that many of Gmail's modules are dynamically loaded, such as chat chat module, in the initialization of the page is not loaded, and even after the load error, the other parts of the page can be used normally.

4. Can your module modules be easily tested?
each of your modules is likely to be used on a large site of millions of users, even more than one site uses it, so your module needs to be able to withstand testing, that is, whether within the framework or outside the framework, should be able to be very simple to test, including most of the assertions in different environments can be passed.

Long-term consideration

When architecting large programs, it is important to be forward-looking, not only one months or a year from now, but what is the possibility of change in the longer term? Developers often bind the code and programs of DOM manipulation too tightly, although sometimes it's time to encapsulate separate logic into different modules, and think about why it's not good for long.

One of my colleagues once said that a precise architecture may not be suitable for future scenarios, sometimes right, but when you need to do it, the money you pay is quite much. For example, you may be due to some performance, security, design reasons need to choose between Dojo, JQuery, Zepto, Yui need to select the replacement, this time there are problems, most of the modules are dependent on the need for money ah, need time ah, need people ah, right?

For some small sites are fine, but large sites do need to provide a more flexible mechanism, rather than worry about the various problems between the modules, since saving money, but also save time.

To sum up, now can you be sure that you can replace some libraries without rewriting the entire program? If not, it is better for you to estimate what we are going to say below.

Many experienced JavaScript developers have given some key notes:

Javascriptmvc's author, Justin Meyer, said:

The great secret of building large programs is never to build large programs, but to break them down into smaller modules so that each small module can be tested, scaled, and integrated into the program.

High-performance JavaScript Websites Author Nicholas,zakas:
"The key is to acknowledge from the start," you have No. When you accept this you don ' t know everything, you are begin to design the system defensively. You identify the key areas this may change, which often are very easy to put a little bit to it. For instance, your should expect that the "any" of the app that is communicates with another system'll likely change Need to the abstract that away. "-

A lot of text problems, too much trouble, summed up a sentence is that everything is variable, so to abstract.

JQuery Fundamentals author Rebecca Murphey:
The more closely the links between the modules, the less reusability, the greater the difficulty of change.

These important points of view are the core elements of building a framework that we need to keep in mind.

Brain Storm

Let's brainstorm. We need a loosely coupled architecture with no dependencies between modules and programs to communicate, and then the middle tier takes over and processes feedback for corresponding messages.

For example, if we have a JavaScript build online bakery program, a module sends out a message that "there are 42 pieces of round bread needed to send". We use a different layer layer to process the message from the module, as follows:

module does not directly access program core
The module does not directly call or affect other modules

This will prevent us from making all the modules go wrong because of a module error.

Another problem is security, the real truth is that most people don't think that internal security is a problem, and we say in our own minds that the program is built by myself, that I know what is public, that it's private, that security is okay, but do you have a way to define which module can access the program core? For example, there is a chat chat module, I do not want him to call the admin module, or do not want it to invoke the DB Write permissions module, because this is very fragile between, can easily lead to XSS attacks. Each module should not be able to do everything, but the current JavaScript code in most architectures has this problem. Provides a middle tier to control which module can access the authorized part, that is, the module is only able to do what we authorize.

Proposed architecture

The focus of our article is that the architecture we propose this time uses the design patterns we are all familiar with: module, façade, and mediator.

Unlike the traditional model, in order to understand the decoupling of each module, we only allow the module to release some event events, the Mediator mode can be responsible for subscribing message messages from these modules, and then control the Response,facade mode of the notification user to limit the permissions of each module.

Here are some of the parts that we should be careful to explain:
1 design mode
1.1 Modular theory
A summary of 1.1.1
1.1.2 Module Mode
1.1.3 Object Self-level
1.1.4 COMMONJS Module
1.2 Facade mode
1.3 Mediator Mode
2 Application to your architecture
2.1 Façade-Core abstraction
2.2 Mediator-Program core
2.3 Working closely together

Modular theory

You may have more or less used modular code, modules are part of a complete robust program architecture, each of which is created for a separate purpose, back to Gmail, and for example, the chat chat module appears to be a separate part, in fact it has a lot of separate sub modules to make up, For example, the expression inside the module is actually a separate child module, is also used to send the mail window.

Another is that modules can be dynamically loaded, deleted, and replaced.

In JavaScript, there are several ways to implement modules, known as module patterns and object literals, and if you are already familiar with them, skip to the Commonjs section.

Module mode

Module mode is a more popular design pattern, it can encapsulate private variables, methods, state, by wrapping these content, the general global object can not directly access, in this design mode, only return an API, all the other content is encapsulated into private.

In addition, this pattern is similar to a function expression that is executed, except that the module returns an object, and the function expression is returned by the form.

As we all know, JavaScript does not want the same access modifiers as other languages, and cannot declare private,public modifiers for each field or method, how do we achieve this pattern? That is return an object, which includes some open methods that are capable of invoking internal objects.

Look, the following code, this code is a custom code, the declaration includes a global object Basketmodule, the basket array is a private, so your entire program is not access to this private array, and we return an object, It contains 3 methods, such as Additem,getitemcount,gettotal, that have access to a private basket array.

var Basketmodule = (function () {
var basket = [];//private return
{//exposed to public
additem:function (v)  alues) {
Basket.push (values);
},
getitemcount:function () {return
basket.length;
},
gettotal:function () {
var q = this.getitemcount (), p =;
while (q--) {
p+= basket[q].price;
} return
p;
}
}
());

Also note that the object we return is directly assigned to Basketmodule, so we can use it as follows:

Basketmodule is a object with properties which can also be methods
Basketmodule.additem ({item: ' Bread ', price:0.5} );
Basketmodule.additem ({item: ' Butter ', price:0.3});
 
Console.log (Basketmodule.getitemcount ());
Console.log (Basketmodule.gettotal ());
 
However, the following won't work:
Console.log (Basketmodule.basket);//(undefined as not inside the returned obj ECT)
Console.log (basket);//(only exists within the scope of the closure)

How do you do that in every popular class library (such as Dojo, JQuery)?

Dojo

Dojo tries to use Dojo.declare to provide a class-style declarative way that we can use to implement module mode, for example if you want to declare basket objects under the store namespace, you can do this:

Traditional way
var store = Window.store | | {};
Store.basket = Store.basket | | {};
 
Using Dojo.setobject
dojo.setobject ("Store.basket.object", (function () {
var basket = [];
function Privatemethod () {
console.log (basket);
Return
{
publicmethod:function () {
privatemethod ();
}}
;
()));

Combined with dojo.provide, it's very powerful.

YUI

The following code is Yui's original way of implementation:

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 m Ethod. ");

  Within Basket, I can access "private" VARs and methods:
  YAHOO.log (Myprivatevar);
  YAHOO.log (Myprivatemethod ());

  The native scope of Mypublicmethod are store so we can
  //access public members using ' this ':
  YAHOO.log (THIS.MYP Ublicproperty);}}

(); 

Jquery

jquery has many implementations of the module schema, so let's look at a different example where a library function declares a new library and then automatically executes the Init method in Document.ready when the library is created.

function library (module) {
$ (function () {
if (module.init) {
module.init ();
}
);
return module;
}
 
var mylibrary = library (function () {return
{
init:function () {
/*implementation*/
}
};
} ());

Object self-Surface quantity

The object's surface volume uses curly braces, and you don't need to use the New keyword when you use it, but you can use this approach if you don't care about the publice/private of a property field in a module, but note that this is different from JSON. Object Self-Volume: var item={name: "Tom", value:123} json:var item={"name": "Tom", "Value": 123}.

var mymodule = {
 myproperty: ' somevalue ',
 //object literals can contain properties and methods.
 Here, another object are defined for configuration
 //purposes:
 myconfig: {
 usecaching:true,
 language : ' En '
 },
 //a very basic Method
 Mymethod:function () {
 console.log (' I can haz functionality? ');
 } ,
 //output A value based on the current configuration
 mymethod2:function () {
 Console.log (' Caching is: ' + (thi s.myconfig.usecaching)? ' Enabled ': ' disabled ');
 },
 //override the current configuration
 mymethod3:function (newconfig) {
 if (typeof newconfig = = ' object ') {
  this.myconfig = newconfig;
  Console.log (this.myConfig.language);
 }
 }
;

 
Mymodule.mymethod (); I can haz functionality
mymodule.mymethod2 ()//outputs enabled
mymodule.mymethod3 ({language: ' fr ', Usecaching:false}); Fr

Commonjs

About the introduction of COMMONJS, here is not much to say, before a lot of articles have introduced, we want to mention here is the COMMONJS standard has 2 important parameters exports and require,exports is to load the module, Require means that the modules that are loaded need to rely on other modules, and they need to be loaded in.

/*
Example of achieving compatibility with AMD and standard commonjs by putting boilerplate around the standard Common    JS module Format:
*/
 
function (define) {
define (function (require,exports) {
//module Contents
var dep1 = require ("Dep1");
Exports.someexportedfunction = function () {...};
//...
});
}) (typeof define== "function" Define:function (Factory) {Factory (Require,exports)});

There are a lot of COMMONJS standard module load implementation, I prefer the requirejs, it can be very good loading modules and dependent modules, to a simple example, such as the need to convert the image into ASCII, we first load the Encoder module, Then get his encodetoascii method, theoretically the code should be as follows:

var encodetoascii = require ("encoder"). Encodetoascii;
Exports.encodesomesource = function () {
//other operations, then call Encodetoascii
}

But the code above does not work because the ENCODETOASCII function is not attached to the Window object, so it cannot be used, and the following code needs to be improved:

Define (function (Require, exports, module) {
var encodetoascii = require ("encoder"). Encodetoascii;
Exports.encodesomesource = function () {
//process then call Encodetoascii
}
});

Commonjs potential is great, but because the uncle is not very familiar, so not too much introduced.

Facade mode

The façade pattern plays an important role in this framework, and there are a number of JavaScript libraries or frameworks in this pattern, the biggest of which is to include the high level API to hide specific implementations, which means that we only expose interfaces, Internal implementation we can make their own decisions, but also means that the internal implementation of the code can be easily modified and updated, such as today you are using jquery to achieve, tomorrow and want to change Yui, which is very convenient.

In the following example, we can see that we provide a lot of proprietary methods and then expose a simple API to allow the outside world to execute calls to the internal method:

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

The difference between the façade and the mediator below is that the façade provides only existing functionality, while mediator can add new functionality.

Mediator Mode

Before we talk about Modiator, let's take for example, the airport flight control system, the legendary tower, has the absolute right to control the takeoff and landing time and place of any aircraft, and the aircraft and aircraft are not allowed to communicate before, that is, the tower is the core of the airport, Mediator is equivalent to this tower.

Mediator is used in the program has multiple modules, and you do not want to let each module have dependencies, that through the mediator mode can achieve the purpose of centralized control. In the real world, mediator encapsulates a lot of modules that they don't want to do, lets them connect through mediator, and also loosely coupling them so that they have to communicate through mediator.

What are the advantages of the mediator model? That's decoupling, and if you know more about the observer model, it's a lot simpler to understand the mediator diagram below, a mediator pattern with a higher level:

Consider that each module is a publisher, and mediator is both a publisher and a subscriber.

Module 1 Broadcasting to mediator an actual, say need to do sth.
Mediator after capturing the message, immediately start processing the message required to use the Module 2,module 2 processing end of the return information to mediator
At the same time, mediator also started Module 3, which automatically logs log into module 3 when accepting Module 2 to return messages

As you can see, there is no communication between the modules, and mediator can also implement the ability to monitor the state of each module, for example, if Module 3 fails, mediator can temporarily just want to other modules, then restart Module 3, and then continue execution.

In retrospect, you can see that the advantages of mediator is that the loosely coupled modules are controlled by the same mediator, the module only needs to broadcast and listen to events, and the modules do not need direct contact, in addition, a single message processing can use multiple modules, It is also convenient for us to add new modules to the existing control logic in the future.

OK: Since all modules are directly unable to communicate directly, all of the relative performance aspects may be slightly reduced, but I think it is worthwhile.

We have based on the above explanation to a simple demo:

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;
 }
 ;
 
} ());

Then there are 2 modules that are called separately:

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.

Application façade: Abstraction of the application core

A façade works as an abstraction in the core of an application, communicating between mediator and modules, which can only be communicated through the façade to the core of the program. The duty of abstraction is to ensure that the modules are provided with a consistent interface (consistent interface) at all times, similar to the Sendbox controller roles. All module components communicate through it and mediator, so the façade needs to be reliable and trustworthy, and as a function of providing an interface to the module, the façade also needs to play another role, which is security control, which is deciding which part of the program can be accessed by a module, Module components can only invoke their own methods and do not have access to any unauthorized content. For example, a module might broadcast Datavalidationcompletedwritetodb, where security checks need to ensure that the module has write access to the database.

In short, mediator can only be processed after a façade authorization test.

Application mediator: The core of the application

Mediator is working as an application core role, let's briefly describe his responsibilities. The core task is to manage the lifecycle of the module (lifecycle), and when the core catches any information in it, he needs to judge how the program is handled-that is, deciding which or modules to start or stop. When a module starts to start, it should be able to automatically execute, without the application core to determine whether the execution (for example, whether to do in the DOM ready), so that the need to determine the module itself.

You may also have a problem, that is, a module under what circumstances will stop. When a program detects a module that fails, or is wrong, the program needs to make a decision to prevent the method from continuing executing in the module so that the component can be restarted, primarily to improve the user experience.

In addition, the core should be able to dynamically add or remove modules without affecting any other functionality. A common example is that a module is not available at the beginning of the page load, but after the user operation, the need to dynamically load the module and then execute, like Gmail in the chat chat function, from the goal of performance optimization, it should be very good understanding of it.

Exception handling, which is also handled by the application core, also broadcasts any errors to the core when broadcasting the information, so that the program core can stop/reboot the modules as appropriate. This is also a very important part of the loosely coupled architecture, and we don't need to manually change any of the modules, and we can do this by mediator using the Publish/subscribe.

Assemble it.

Each module contains a variety of functions in the program, they have information to be processed, release information notification procedures (this is their primary responsibility), the following QA section mentioned that the module can rely on a number of DOM tools to operate methods, but should not and the system's other modules have dependencies, A module should not focus on the following:

1. Which object or module subscribes to the information published by this module
2. Whether these objects are client objects or server-side objects
3. How many objects subscribe to your information

The façade abstracts the core of the application, avoids direct communication between modules, subscribes to information from each module, and is responsible for authorization detection, ensuring that each module has its own separate authorization.

The Mediator (application core) plays the role of the Publish/Subscribe manager using the Mediator mode, responsible for module management and start/stop module execution, dynamic loading and restart of faulty modules.

The result of this architecture is that there is no dependency between modules, because loosely coupled applications can be easily tested and maintained, modules can be easily reused in other projects, and can be dynamically added and deleted without affecting the program.

Publish pub/Subscription sub extensions: Autoenrollment Events (Automatic event registration)

For autoenrollment events, you need to follow certain naming conventions, such as if a module publishes an event with a name of Messageupdate, then all modules with messageupdate methods will be automatically executed. There are advantages and disadvantages, specific implementation, you can see me another post: jquery Custom binding Magic upgrade version.

Qa
1. Is it possible to not use a façade or a similar sandbox pattern?

Although the architecture outlines the ability of a façade to implement a licensing check, it is entirely possible for the mediator to do what the lightweight architecture does is almost identical, decoupling, and ensuring that the modules are directly communicating with the application core is OK.

2. You improve the module directly can not rely on, whether it means that you can not rely on any Third-party class libraries (such as jquery).

This is actually a two-sided problem, as we mentioned above, a module may have some sub modules, or a basic module, such as the basic DOM Operation Tool class, at this level, we can use the third party class library, but please make sure that we can easily replace them.

3. I like this architecture and would like to start using this architecture, is there any code samples to refer to?

I'm going to get a code sample for your reference, but before that you can refer to Andrew Burgees's post writing Modular JavaScript.

4. Is it feasible if the module needs to communicate directly with the application core?

Technically, there is no reason the module cannot communicate directly with the application core, but for most application experiences, it is not. Now that you have chosen this architecture, you must follow the rules defined by that schema.

Thanks

Thanks to Nicholas Zakas's original stickers, summed up the ideas together, thank Andree Hansson Technical review, thank Rebecca Murphey, Justin Meyer, John Hann, Peter Michaux, Paul Irish and Alex Sexton, all of them provided a lot of information related to this session.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.