Introduction This talk will cover strategies that can help streamline your application architecture. Thanks to Nicholas Zakas, Rebecca Murphey, Paul Irish and Justin Meyer for their previous work in this area.
What is a ‘large’ application? Some developers suggested: “Apps where the size of code is over over 100,000 LOC” Incorrect, because code size does not always correlate to complexity
What is a ‘large’ application? “Apps with over 1MB of JS code written in-house” Again, this could be very simplistic code. Can we get more clear?
What is a ‘large’ application? “A non-trivial application requiring signiﬁcant developer effort to maintain” Correct.
Possible problems with this: How much of this architecture is instantly re-usable? Can single modules exist on their own independently? Are they self-contained?
Possible problems with this: How much do modules depend on other modules inside the same system? Does your app contain many modules that are tightly coupled?
Possible problems with this: How easily can you test individual modules? How certain are you that if speciﬁc parts of your application fail, it can still function?
Think long-termDevelopers often couple their DOM manipulation codequite tightly with the rest of their applicationWhy is this not a good idea if we’re thinking long-term?
Think long-termYou may decide to switch from using jQuery to Dojo orYUI for reasons of performance, security or design.Can this decision currently be made without rewritingyour entire application?
Remember..“The secret to building large apps is never build largeapps. Break your applications into small pieces. Then,assemble those testable, bite-sized pieces into yourbig application” - Justin Meyer.
Also.. “The more tied components are to each other, the less reusable they will be, and the more difﬁcult it becomes to make changes to one without accidentally affecting another” - Rebecca Murphey.
Let’s brainstorm. We want a loosely coupled architecture with functionality broken down into smaller modules that aren’t dependant on one another. You probably already use modules but we need them to be fully independent entities.
Some more ideas.To achieve this we need single modules to speak tothe rest of the application when something interestinghappens.We then use a different layer to interpret requests sothat modules don’t directly access the core.This aids in preventing applications from falling overdue to errors with a speciﬁc module.
and wrapping up..Modules shouldn’t be able to access everything.They probably can in most current architectures.Having an intermediate layer handle permissions forwhich modules can access which parts of yourframework gives you a layer of security.This means a module is only able to do at most whatwe’ve permitted it do.
Module Theory “Anything can be deﬁned as a reusable module” - Zakas. Modules should be considered independent units of functionality that can exist on their own
Module Theory Modules have very limited knowledge of what’s going on in the rest of the application Loose coupling is essential to this - modules should ideally not depend on other modules.
Loose CouplingFacilitates improved maintainability by removing codedependencies where possibleIn our case, modules should not rely on other modulesin order to function correctly.When used effectively, it’s straight-forward to seehow changes to one part of a system may affectanother.
The Module PatternThe well-known module pattern makes use ofclosures to bake privacy, state and organization intoyour objects.It’s quite similar to an IIFE with an object returnedinstead of a function.Commonly implemented as a singleton.
Object Literals Object literal notation is another option for structuring modules Basically consists of an array of key:value pairs Methods deﬁned using object literal notation don’t exist until the execution of the script.
Facade Pattern Provides a convenient higher-level interface to a larger body of code, regardless of underlying complexity Hides the inner-workings of a library or set of modules, allowing the implementation to be less important. We thus only need to interact with the facade rather than the subsystem it encompasses.
The Mediator PatternEncapsulates how disparate modules interact witheach other by acting as an intermediaryPromotes loose coupling by preventing objects fromreferring to each other explicitly, solving our moduleinter-dependency issues.
The Mediator PatternAllows for actions of each module to varyindependently, so it’s extremely ﬂexibleSomewhat similar to Observer/Pub-Sub, so it’s notdifﬁcult to understand how it ﬁts in if you’ve used one ofthese patterns previously.
Why is it the bee’s knees? Allows modules to broadcast or listen for messages without worrying about the rest of the system Messages can be handled by any number of modules at once. Typically signiﬁcantly more easy to add or remove features to systems which are loosely coupled like this.
Mediator downsidesBy adding a mediator between modules, they mustalways communicate indirectly. This can cause a veryminor performance drop.Because of the nature of loose coupling, it’s difﬁcult toestablish how a system might react by only looking atthe broadcasts.At the end of the day, tight coupling causes all kinds ofheadaches and this is one solution.
Mediator Metaphor Think of an airport control tower. The tower handles what planes can take off and land. All communications are done from the planes to the control tower, rather than from plane-to- plane A centralised controller is key to the success of this system as is the case with the mediator pattern.
The FacadeEffectively, an abstraction of the application core thatsits in the middle between it and modulesEnsures a consistent interface to our modules isavailable at all timesShould be the only thing modules are aware of - theyshouldn’t know about other components.
The FacadeComponents communicate via the adapter so it needsto be dependableThe adapter acts as a security guard, determiningwhich parts of the application a module can accessComponents only call their own methods andshouldn’t interact with anything they don’t havepermission to
NOTE:Nicholas Zakas refers to the facade here as a sandboxcontroller.Agrees that it could equally be considered the adapter,proxy or facade pattern.As Stoyan Stefanov deﬁned an existing ‘sandbox’pattern, I refer to the sandbox as a facade as IMO thispattern matches it’s purpose most closely.
The application core It’s job is to manage the module lifecycle. When is it safe for a module to start? When should it stop? Modules should execute automatically when started.
The application core It’s not the core’s job to decide whether this should be when the DOM is ready. The core should enable adding or removing modules without breaking anything. It should ideally also handle detecting and managing errors in the system.
Revised Architecture • Your new architecture could potentially look something like this: PublishersLibraries Self-contained modules Adapter (abstraction of the core) Mediation Subscribers Application Core (Mediator) tied into MVC if using that pattern.
Tying in: modules Modules want to inform the application when something interesting happens. eg. a new message has arrived.other modules related as necessary. Correctly publishing events of interest should be their primary concern.
Tying in: the facade The core (via the abstracted adapter) listens out for interesting events and says ‘Awesome. What happened? Give me the details’.
Tying in: the core The core will then react/start or stop other modules related as necessary.
Modules Should ideally not be concerned about: what objects or modules are being notiﬁed where these objects are based (client? server?) how many objects subscribe to notiﬁcation
Module Communication ‘Tom’s order has been successfully packaged Module 1 OrderPackager Adapter notify(‘orderPackaged’,‘Tom’,‘success’) M Start LabelManager Core Module 2 LabelManager ‘Tom’s order has been labelled notify(‘orderLabelled’,‘Tom’, ‘success’) Start DispatchManager Module 3 DispatchManager ‘Tom’s order has been dispatchednotify(‘orderDispatched’,‘Tom’, ‘success’,’12-08-11’)
Summary The core acts as like a ‘Pub/Sub’ manager using the mediator pattern. Responsible for module management. The facade abstracts the core to avoid modules touching it directly. Handles security. The modules contain speciﬁc pieces of functionality for your application.
ResultModules are no longer dependent on anyone.If you stick to a consistent API, you can easily replacea module using jQuery with one using dojo later on.
ResultModules can be easily tested and maintained ontheir own.Modules can be added or removed without theapplication falling over.