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 use hilary with AMD (Asynchronous Module Definition): namely, require.js.

We have a single Model View Controller structure that is tied together with a composition root. We also mock jQuery ajax, to get data into the model, without going ot the server, to further display the uses of the IoC container.

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 transactional app, each page might have a Composition Root, although your app would likely be easier to maintain if you follow the same pattern that makes sense in a SPA, which would be to have a single Composition Root file. I tend to name our Composition Root compositionRoot.js, startup.js or appStart.js.

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

// Using AMD, our modules registered with some of their dependencies already 
// resolved. That's a bit of mix-and-match, and you could avoid it by only 
// defining factories. Personally, I would go that route, but this example 
// assumes you are already steeped in AMD, so I'm going for a realistic scenario.

define('compositionRoot', ['hilary', 'mock-ajax', 'model', 'view', 'controller'], 
	function(hilary /*, mockAjax, myModel, myView, myController */) {
		"use strict";

		var _mockAjax, _controller;

		// resolve a mock jQuery ajax service
		// jQuery was already injected into it by require.js
		_mockAjax = hilary.resolve('mock-ajax')
			.makePromise([{ id: 1, name: 'Hilary Page' }, { id: 2, name: 'Ole Kirk Kristiansen' }], 40);

		// resolve myController and inject our _mockAjax singleton, 
		// model factory and view factory into it. In a non-test 
		// scenario, we would inject $.ajax instead of _mockAjax.
		_controller = function() {
			return hilary.resolve('myController')
				.call(null, 
					_mockAjax, 
					hilary.resolve('myModel').call(), 
					hilary.resolve('myView').call());
		};

		return {
			start: function() {
				_controller().view();
			}
		};
	});

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: model.js, view.js, controller.js and mock-ajax.js. All four of these services self-register themselves with hilary when they 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. In this example, we reference a singleton (_mockAjax) and two factories: the composed model and view services. We resolve "myController" with three, already-composed services.

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.