Dependency Injection in Java Script

No, You Don't Need Any "Framework" at All!

Posted by Heikki Kupiainen/Oppikone on 11.12.2016 07:15:58

There has been lately some talk about dependency injection support in different JavaScript frameworks. I even saw a funny article comparing JQuery and AngularJS, concluding that AngularJS, unlike JQuery, "supports dependency injection". Seriously! A statement like that can't possibly be formulated without extreme poverty of imagination. The great secret is:

You don't need any framework at all to have dependency injection.

In JavaScript, like in any dynamic language, dependency injection design pattern actually is a feature so inherent to the very programming concept that chances are it never crosses your mind to start thinking about it at all. I am going to demonstrate dependency injection in vanilla JavaScript, which means without any framework. In the following example I will create a controller with an injected dependency. Yes, a true controller as in true MVC (Model-View-Contoller) pattern. The case with MVC design pattern is just the same as with "dependency injection". You don't really need any framework to use MVC either. You just need some basic programming skills and a little bit imagination!

Now, I am going to write a "Student List Controller" that gets the lecture ID as parameter and renders a list of students enrolled on that lecture:

The code I describe above introduces a Student List Controller that can render a html view containing a student listing. This code is vanilla JavaScript and does not require any additional framework as such. Controller actions can be typically static methods since there is conceptually only one instance of each controller in any given application. Should there be any need to define controllers as instances - which I strongly doubt - that wouldn't be any problem in vanilla JavaScript either. For object oriented programming, have a look at my article on object oriented programming in Javascript.

Please note that StudentListController has a reference to studentListService. Where did studentListService come from? It came through dependency injection. Therefore, StudentListController must have an injection method for the student list service:

This is almost too easy to be true, right? True dependency injection without any framework! Technically, it's actually not necessary to implement any setters to inject a dependendency into an object. You could just write:

StudentListController.templateEngine = myTemplateEngineWhatEver;

However, I don't recommend this practice. It is better to implement setter methods and prefer injecting dependencies only through them. If you are determined to only inject dependencies through explicit setter methods, you will be rewarded later on when the software grows in complexity. If there is a problem with a dependency, for example it is null or something else than you expected, it is then easier to debug by adding a breakpoint inside the setter method and check when and what goes wrong. Just like it is easier for a door man to control ID's of the guests if they enter the restaurant only through the main entrance.

All good so far. Now, someone from planet Java might raise their hand at this point, to voice out their furious objection: "Hey hey hey, how about interfaces?" But that's the fun part of dynamic languages: you don't need interfaces! Interfaces, once considered to be one of most ingenious inventions in Java, actually only solve a self-imposed problem. Interfaces solve a limitation that only exists if the programming language has a requirement for strongly typed data structures! With no types, there is no need for interfaces. No types, no worries!

Conveniently, it is useful to have one centralized location for initializing dependencies. You can basically name the class for this procedure as you wish. I like to have it in App class. It is common that the first class of the Application is named "App". App is the starting point of the whole application. Typically it is initalized in web page's onload event.

Now, this architecture makes it possible to swap implementations for the student list service and the template engine on the fly. For instance, you might want to use some other template engine than EJS. In that case, you only need to replace EJSTemplateEngine with another implementation, for instance MustacheTemplateEngine.. Make sure that both templating engines implement parse function. JavaScript will only complain when it's time to try out! Similarly, when the data format for the student list feed changes, you might need to use XMLRequestStudentListService instead of JSONRequestStudentListService. With dependency injection, you will only need to update the dependency initialization section of the software.


All this being said, it is time for a word of warning. Engineering is fun. Over-engineering is even more fun. It can make you feel noble. But your customer's wallet might have a different opinion. It's a typical beginner's mistake to try to create something extremely configurable, reusable and parametrizable. For instance, you might like to create an architecture that makes it easy to change the template engine on the fly. But chances are there won't be any need to do it during the app's lifetime. You might want to change the service implementation. But chances are there won't be any need for this at all. It is always better to produce real added value to customer that they can benefit from today rather than burning the budget creating features that have theoretical or ideological benefits at best. Don't make your customer pay for your self-education. Teach yourself on your spare time!