Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

Madcap Idea Part 2 : Adding DI/IOC To The Client Side Front End Web Site

5.00/5 (4 votes)
22 May 2017CPOL7 min read 7.5K  
This post will be about adding DI/IOC to the bear bones no thrills client portion of the web site that we built last time.

Last Time

Last time we built a bare bones react/webpack/babel/typescript/bootstrap web site. You can read that post here should you wish to: https://sachabarbs.wordpress.com/2017/05/15/madcap-idea-part-1-start-of-the-client-side-portion-of-the-web-site/

PreAmble

This post will be about adding DI/IOC to the bear bones no thrills client portion of the web site that we built last time. Just as a reminder this is part of my ongoing set of posts which I talk about here :

https://sachabarbs.wordpress.com/2017/05/01/madcap-idea/, where we will be building up to a point where we have a full app using lots of different stuff, such as these

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

What Is DI/IOC?

In software engineering, dependency injection is a technique whereby one object supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

This fundamental requirement means that using values (services) produced within the class from new or static methods is prohibited. The class should accept values passed in from outside.

The intent behind dependency injection is to decouple objects to the extent that no client code has to be changed simply because an object it depends on needs to be changed to a different one.

Dependency injection is one form of the broader technique of inversion of control. Rather than low level code calling up to high level code, high level code can receive lower level code that it can call down to. This inverts the typical control pattern seen in procedural programming.

As with other forms of inversion of control, dependency injection supports the dependency inversion principle. The client delegates the responsibility of providing its dependencies to external code (the injector). The client is not allowed to call the injector code. It is the injecting code that constructs the services and calls the client to inject them. This means the client code does not need to know about the injecting code. The client does not need to know how to construct the services. The client does not need to know which actual services it is using. The client only needs to know about the intrinsic interfaces of the services because these define how the client may use the services. This separates the responsibilities of use and construction.

There are three common means for a client to accept a dependency injection: setter-, interface- and constructor-based injection. Setter and constructor injection differ mainly by when they can be used. Interface injection differs in that the dependency is given a chance to control its own injection. All require that separate construction code (the injector) take responsibility for introducing a client and its dependencies to each other

From https://en.wikipedia.org/wiki/Dependency_injection

What Choices Do We To Do This When Working With React?

There are several choices we have when working with React, such as

  • Using react context
  • Using the module system
  • Using a 3rd party DI/IOC system (I will be covering this, in this post)

There is VERY good post on these different techniques here : https://github.com/krasimir/react-in-patterns/tree/master/patterns/dependency-injection it is a great read and I suggest you take a look to gain a better understanding of some of the more obscure areas of react (context I am looking at you)

Which 3rd Party DI/IOC Library Did I Chose And Why?

I have decided to use the https://github.com/inversify/InversifyJS (Inversify JS) DI/IOC library. Why did I chose this one, well there were quite a few reasons:

  • It is written in TypeScript, and I wanted to use TypeScript where possible
  • Is looks like a good full featured container, that reminds me of many other containers that I have worked with in .NET/Scala
  • It is quite mature and is in version 2.0
  • It has quite a bit of good press around it
  • The documentation is good
  • It did what I wanted

So those were my reasons, so what do we have to do to get Inversify JS to work?

Installation

Node Module Installation

The first steps is to make sure we have the correct node packages, to do this we just need to issue the following NPM (node package manager) command line :

JavaScript
npm install inversify reflect-metadata --save

Once this is done you should have the following entries in your package.json file

image

Changes To The TypeScript tsconfig.json File

The other thing that Inversify JS needs is a couple of specific TypeScript settings. These need to go in the tsconfig.json file, the new lines since last time are these

image

That is all the setup we need, so lets now have a look at using the Inversify JS DI/IOC classes, decorators etc etc

Creating Some Injectable Thing

Lets start with creating something that can be injected with other values, and can be resolved from the container.

In Inversify JS a type has to be marked as @Injectable, which is not something you have to do in other DI/IOC offerings. This is more than likely a requirement because JavaScript is a dynamic language and these decorators are used to create extra metadata to ease in the resolution mechanisms used by ALL IOC containers.

To actual get something injected into a class we need to use the @Inject decorater.

Here is an example of a class that can be resolved from the Inversify JS IOC container, and will also have it constructor dependencies resolves by the Inversify JS IOC container.

JavaScript
import { injectable, inject } from "inversify";
import { TYPES } from "../types";

@injectable()
export class Foo {

    private _num: number;

    constructor(@inject(TYPES.SomeNumber) num: number) {
        this._num = num;
    }

    getNum() {
        return this._num * 2;
    }

}

You can see that we also use some TYPES. What are these, lets take a look at that.

JavaScript
export const TYPES = {
    Foo: Symbol("Foo"),
    SomeNumber: Symbol("SomeNumber")
};

It can be seen this TYPES constant just offers us a way of using Symbol as runtime identifiers for our dependencies.

Ok so now that we have a class that expects to have its dependencies satisfied from the Inversify JS IOC Container, and is itself resolvable, lets see how we can configure the container.

Creating The Container

I have chosen to create a singleton object for my container, which looks like this

JavaScript
import "reflect-metadata";
import { Container } from "inversify";
import { TYPES } from "../types";
import { Foo } from "../domain/Foo";

export class ContainerOperations {
    private static instance: ContainerOperations;
    private _container:Container = new Container();

    private constructor() {
        
    }

    static getInstance() {
        if (!ContainerOperations.instance) {
            ContainerOperations.instance = new ContainerOperations();
            ContainerOperations.instance.createInversifyContainer();
        }
        return ContainerOperations.instance;
    }

    private createInversifyContainer() {
        this.container.bind<number>(TYPES.SomeNumber).toConstantValue(22);
        this.container.bind<Foo>(TYPES.Foo).to(Foo);
    }

    public get container(): Container {
        return this._container;
    }
}

There are a couple of things to note in the above code:

  • We import reflect-metadata, Container and TYPES
  • We have our singleton that simple wraps the Inversify JS IOC container.
  • We configure the the container registrations (bindings in Inversify JS speak)

And that is all there is to that part. So all that is left, is to actually resolve something from the container. We will look at that next

Resolving Something From The Container

As I am using react, I will likely be using the Inversify JS IOC container to assist me with creating the props for the react components. That is not strictly relevant to this discussion, so lets just see how we can resolve an instance of the Foo IOC registered class using Inversify JS

We do this as follows:

JavaScript
import { Foo } from "./domain/Foo";
import { TYPES } from "./types";
import { ContainerOperations } from "./ioc/ContainerOperations"; 

let foo = ContainerOperations.getInstance().container.get<Foo>(TYPES.Foo);

It can be seen that we simple make use of the singleton (that wraps the container) to resolve our Foo class. Happy days

Conclusion

I have to say I did struggle a bit with getting Inversify JS up and running in my project. But I also have to say that I asked a question on the Inversify forum and the author Remo Jansen was absolutely brilliant in helping me to get my stuff to run. To the point where I pointed him at my GitHub repo, and he looked at, got it to work, and sent me a pull request.

Remo I tip my hat to you sir, top library, top fella. And as promised I owe you that beer

So once it was installed, I found it very easy to work with, it soon felt like many other IOC frameworks I have used (NInject, Funq,Munq, Castle, AutoFac, Unity, MEF take your pick). I was very happy with the results.

As I previously stated I will be continuing to write posts which will be tracked on Trello : https://trello.com/b/F4ykCOOM/kafka-play-akka-react-webpack-tasks

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)