::
In this example (check out the source), we implement the same dependency injection pattern as the other examples, using only AMD (Asynchronous Module Definition). Namely, require.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.
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:
require(['jquery', 'ko', 'mock-ajax', 'viewModel', 'view', 'controller'], function( $, ko, mockCtor, viewModelCtor, viewCtor, controllerCtor) { "use strict"; var mocker, ajax, viewModel, view, controller; mocker = mockCtor.init($); // create a singleton mocker ajax = mocker.makePromise([ // create a mock jQuery ajax signature { id: 1, name: 'Hilary Page' }, { id: 2, name: 'Ole Kirk Kristiansen' }], 40 /**/); viewModel = viewModelCtor.init(ko); // create a singleton viewModel view = function() { return viewCtor.init(ko); }; // create a parameterless view factory controller = controllerCtor.init(ajax, viewModel, view); // create a singleton controller controller.action(); // call an action on the controller (i.e. this would load the page content) });
So this app is made up of a model, a view and a controller. It depends on jQuery and knockout. 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, rather than using require's auto-resolution.
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.
Another benefit of this pattern, with respect to how require.js loads the JavaScript files, is that it allows the browser to optimize loading, rather than require.js. This is because, after we load compositionRoot.js, the rest of the files are queued up all at once. If we declare the dependencies in each module, then the files are queued up n-times, depending on how deep the rabbit hole goes.