View on GitHub

Getting started with hilary.js

hello, I'm hilary => a simple JavaScript IoC container.

In this example (check out the source) , we implement a dependency injection pattern using hilary.js.

We have a single Model View Controller structure that is composed by a Composition Root. We also mock jQuery ajax, to get data into the model without going to the server, to demonstrate the ease of testing modules using dependency injection and unit-of-work (UOW) patterns.

output from the example:

::

I'm not going to get into what Compsition Root is, but if you are unfamiliar with the term, Mark Seeman and his book, Dependency Injection in .NET should be your goto reference.

In a single page app (SPA), it makes sense to have a single Composition Root file. In a transactional app, each page might have a Composition Root, although it may be easier to maintain if you also use a single Composition Root and something along the lines of a feature-flag pattern (which I'm not going to get into).

Let's take a look at the Composition Root for this example, to figure out how it works:

// hilary.use lets us pass in window dependencies, similar to the standard 
// module pattern, except that the dependencies and the function arguments 
// that they resolve to are right next to each other. 
//
// The first argument is always the container, then the window dependencies 
// are passed in, in order.
//
// hilary.use is optional. You could use a standard module pattern, 
// require.js, or whatever you want.

hilary.use([hilary, jQuery, ko, window], function(hilarysInnerContainer, hilary, $, ko, window) {

	var _mockAjax, _viewModel, _viewFactory, _controller;

	// Resolve a mock jQuery ajax service.
	// We inject the "mock-ajax" service with a jQuery instance. Then we 
	// make a fake promise that will be used instead of real ajax.
	_mockAjax = hilary.resolve('mock-ajax')
		.init($)
		.makePromise([{ id: 1, name: 'Hilary Page' }, { id: 2, name: 'Ole Kirk Kristiansen' }], 40);

	// Resolve a ViewModel singleton, which we will pass to the controller
	_viewModel = hilary.resolve('myViewModel').init(ko);

	// Create a parameterless factory that resolves a new instance of 
	// "myView" when it is called so the controller can get a new View 
	// each time an action is called, and know nothing about the 
	// View's dependencies
	_viewFactory = function() { return hilary.resolve('myView').init($, ko); };

	// Resolve myController and inject our _mockAjax singleton, 
	// an instance of the model factory and an instance of the 
	// view factory into it.
	// In a non-test scenario, we would inject $.ajax instead of _mockAjax
	_controller = hilary.resolve('myController')
		.init(_mockAjax, _viewModel, _viewFactory);

});

So this app is made up of a model, a view and a controller. It depends on jQuery and knockout (and hilary :). We created four service modules: viewModel.js, view.js, controller.js and mock-ajax.js. Each of those services opts to declare it's dependencies in an init function, and self-registers itself with hilary upon load.

In a compiled language, we would register interfaces and implementations in our Composition Root, but because JavaScript is not a compiled language, I took a cue from AMD and decided on self-registration. During registration, all of the modules declare their dependencies, but these dependencies are unresolved at the time of registration.

In Composition Root, we not only resolve the services that we intend to use, but also inject appropriate dependencies. We compose (i.e. construct or initialize) our services into singletons (the view being an exception) that are used to compose the services that depend on them. The view returns a parameterless contstructor, so the controller doesn't need to know anything about it's dependencies, and also so the view does not maintain state in the event that the same action is called multiple times with different parameters (i.e. usersController.getUser(1) and usersController.getUser(5)).

By taking this approach, it is easy to swap out services for new ones. This can be useful in testing, refactoring, reuse, etc. The controller service uses jQuery ajax, but because we passed in a mock service that satisfies the signature, we are able to isolate behavior by changing a single module: compositionRoot.js.