#dijonjs demo included in todomvc labs section
So what does it do?
In the demo Dijon is used on top of JQuery to provide structure and dependency injection for the app. It allows you to decouple several functional units and let them work together and communicate.
Even though Dijon does not impose a typical MV* structure in any way, it allows you to easily do so w/o forcing you to use any specific paradigm.
In the demo I chose an approach that’s very close to the one used in Robotlegs, which is a MVCS framework for AS3. A
service tier is added to the traditional 3. It’s (amongst others) responsible for all external communication: retrieving data from remote services, saving data to the LocalStorage etc. You could consider it a mediating tier between the app and the outside ‘world’.
One major advantage of Dijon is the ability to centralize ALL dependency configuration (wiring). One of the problems that IOC/DI frameworks aim to solve is dependency management. Class A depends on class B depends on class C etc. This easily leads to all kinds of difficulties: when to instantiate what class, how do you pass the instance to the dependent actors, memory leaks of orphaned instances, …
In Dijon the tiers communicate with each other through events and actors expose “outlets” in which other actors get injected. This means that these objects are completely unaware of each others life-cycles and just know one thing: “when I need it, it’ll be there”. What happens before and after their use of a specific actor is something they don’t have to care about. At all.
This leads to an extremely flexible setup with completely decoupled actors and centralized wiring. One of the benefits of having a centralized dependency configuration is that you always know where to look and add things. When you create a new actor, all you need to do is make sure its source file is included and then wire it up in your config file, where you have a nice overview of all the actors and how they relate and communicate with each other.
Here’s a snippet from the config file of the demo (edited for brevity):
(you can find the full config file here)
//wiring this.system.mapSingleton( 'todosModel', ns.models.TodosModel ); this.system.mapSingleton( 'storageService', ns.services.LocalStorageService ); this.system.mapSingleton( 'footerView', ns.views.FooterView ); //Handlers this.system.mapHandler( 'TodoFormView:addTodo', 'todosModel', 'add' ); this.system.mapHandler( 'StorageService:retrieveCompleted', 'todosModel', 'setList' ); this.system.mapHandler( 'TodosModel:todosListUpdated', 'listView', 'render' ); this.system.mapHandler( 'App:startup', 'storageService', 'retrieve' );
the first part sets up the dependency injection. In it the constructor function
ns.models.TodosModel is registered to the key
todosModel. Every actor that is registered with the Dijon system and that has a
todosModel member will get the instance of
ns.models.TodosModel injected into it.
[FYI: this happens automatically because of this line in the config file
this.system.autoMapOutlets = true;, by default it's
false and then you need to map the outlets 'manually'.]
The second part sets up the event handlers and this is where it gets really interesting.
Actors dispatch events and these can be relayed to methods in other actors.
For example: the
TodosModel exposes an
function add( vo ) method. The
TodoFormView dispatches an event
TodoFormView:addTodo with a todo VO as a payload. In Dijon you can funnel that payload object directly to
TodosModel#add method, with
this.system.mapHandler( 'TodoFormView:addTodo', 'todosModel', 'add' );
In other words: when the event
TodoFormView:addTodo is dispatched, call the
add member of the instance registered to the key
todosModel and pass the event payload as an argument to that method.
There’s no need for an intermediate command or anything. The two actors communicate with each other yet they don’t realize this. The view simply dispatches an event. The model exposes an
In the config file these two are wired to each other. Since the model does not register the add method as an event listener in the view directly, there’s no direct depencency from the model to the view, which is exactly what we want. Actors that have as few dependencies on other actors as possible.
This means that commands and controller classes/functions are a rare commodity in Dijon. They are used for complex model data retrievals, where data needs to be collected from various models, for instance.
[That's why the demo doesn't include any controllers/commands. Since it's such a simple app, the data is sent from one tier to another without the need of intermediate massaging or collection.]
This is an extremely flexible and dynamic way of passing data between dumb, decoupled actors, which seems very appropriate to me in a language that’s dumb and dynamic itself.