Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Help with "Scalable JavaScript Application Architecture"

I am building a large javascript application and I decided to use Nicholas Zakas' scalable application architecture design: http://developer.yahoo.com/yui/theater/video.php?v=zakas-architecture

According to his system, modules are self-encapsulated and do not know about each other... However I have come across many instances in my project where it seemed necessary for modules to know about each other, because they are in essence, the individual parts of a bigger whole.

For example.. I have three modules: Upload, Window, and Manager.

When clicking on an upload option, a pop-up window opens with an upload form. Also a link is on the window "manager".

Clicking the manager link updates the pop-up window to display admin tools...

...

It made the most sense for me to do (pseudo code):

upload module:
  upload option click --> sandbox.notification('need pop up window', [...html markup for form...])

manager module:
  manager link click --> sandbox.notification('need pop up window', [...html markup for admin tools...])

window module:
  sandbox.listen('need pop up window') --> calls createPopUpWindow( passed in html markup  )

... However this goes against the philosophy because the upload and manager modules are specifically "requesting" the window module to do something, therefore they know about it...

So, the only other way I can think of to do this would be:

upload module:
  upload option click --> sandbox.notification('upload option clicked', [...html markup for form...])

manager module:
  manager link click --> sandbox.notification('manager link clicked', [...html markup for admin tools...])

window module:
  sandbox.listen('upload option clicked') --> calls createPopUpWindow( passed in html markup  )
  sandbox.listen('manager link clicked') --> calls createPopUpWindow( passed in html markup  )

.. But that feels a lot less intuitive, and honestly I think it makes my code a lot less clear, because looking at upload module's notification 'upload option clicked', doesn't tell me at all what that is supposed to happen when it's clicked.. I have to go hunting in all my other files for modules that are listening for it..... Which I guess can be viewed as a benefit, because multiple modules might want to respond to 'upload option clicked', where as 'need popup window' could only obviously be addressed by one module.

But when going that approach, it starts to make less sense to me to have my upload module be passing a bunch of html markup pertaining to a pop up window that it doesn't know about, and it starts seeming like the window module should be in charge of generating that markup--- but a lot of the markup is "upload" specific, and the markup has event listeners bound to functions within the upload module--- so having that in the window module isn't really logical... so it starts getting very confusing as to what is the best way to structure all of this.

I also have another situation which is even more problematic.. Two modules: Track and Container. The container has many tracks, and originally I just had the track functions internally part of the container module-- but as the length of code in the module started growing, I decided to separated these into their own modules for clean-code's sake... Anyway, because the container needs to know about it's tracks and to be able to reference them internally, the only way I could set this up was to do:

containerObject = function(name) {
    this.name                       = name;
    this.video_track                = {'name': 'video',   'markup': sandbox.notification('create-track', 'video')}
    this.audio_track                = {'name': 'audio_1', 'markup': sandbox.notification('create-track', 'audio')}
    ....etc....
};

So the Track module is doing a sandbox.listen('create-track') and points that to a function which returns a new track object of the given type..... Maybe it's just not worth it to have track be it's own module...... Since that is the only place where I am assigning a value based off of a notification call.

I'd love to hear what other programmers familiar with pub/sub architecture have to say about this topic......

Please give me your thoughts & advice.

Thank you.

like image 392
patrick Avatar asked Apr 21 '11 05:04

patrick


1 Answers

There are a number of patterns that deal with inter-object communications -- and that is what your real problem is: communication.

Your problem can be distilled into:

  1. You want functionalities to be broken down into modules
  2. You want modules to be as self-contained as possible, with as few links outside as possible
  3. Modules need to work with other modules in a "grand scheme of things" however

No.3 is what was giving you issues -- you want modules to be independent, but then it need to communicate with other modules in order for your program to work.

A typical solution will be for the module to open up "standardized" communication channels to the outside world. It should not care (or matter) how many, which, where, or what objects are on the other side of those channels. It just takes commands from input channels, and sends notifications to output channels. A wonderful side-benefit is ability to unit-test the module easily.

Notice that your modules should not care about the other side -- the Four W's

what -- shouldn't care what classes or objects it is talking to (or listening to)

where -- shouldn't care where the other side is (server? or on the same browser)

which -- shouldn't care which paticular object it is communicating with (the president, or just a worker)

how many -- shouldn't care how many objects it is talking to/listening to simultaneously

Then you wire the whole graph up with a master configuration. This is the basic concept behind Dependency Injection.

As for the plumbing, there are a few ways/patterns that this can be done:

  1. Events and handlers
  2. Publish/subscribe
  3. IOC/DI container
  4. Combo of above
like image 199
Stephen Chung Avatar answered Oct 04 '22 14:10

Stephen Chung