General Concepts

Introduction

Dependency injection is a common technique in software development.

If you are not familiar with it, and you are curious about technical details, you might find more information on this wiki page:

With dependency injection, you get to delegate the responsibility of creating the objects that encapsulate the logic of your system, basically ConanJs will create the objects for you, the objects created this way are called beans

Dependency Injection is a bit like testing, if you are new to it and have never tried it, it will look intimidatory and unnecessary.

But similar to testing, once that you try it and understand it, there is no turning back.

If this is your first contact with dependency injection, we would suggest to head first to Creating the context to get some hands on perspective.

In summary:

  • You will describe what objects you want to create through bean definitions

  • You will pass a key value pairs of bean names to bean definitions to the ContextFactory

  • The context factory will resolve by name the objects to be created according to your bean definitions and create the beans

  • Finally the beans will be accessible through the context.

Let's have a look at each of these terms

The Beans

A bean is a plain JS object that is resolved on the back of the bean definitions that you provide to the DiContextFactory.createContext.

In other words, the bean is the final object that ConanJs creates for you and that it will be available from the context.

The name bean is taken from the Java World.

Some people will argue that we named it like this so we could say that this is the first framework ever with TACOS and BEANS... they might be right.

A rare capture of ConanJs providing Beans through the context

The context

The Context is a plain object that contains the beans.

The context is created through the DiContextFactory.createContext, this method receives the key value pairs of bean names to bean definitions

After the context is created, the beans can be accessed directly by referring to them by their bean name.

Ultimately, on your code you would be importing the context and accessing the beans from there.

This decoupling is the essence of the dependency injection, on your code you don't know where these objects are coming from, you just declare that you need them through the context.

Since the context wraps the beans, we were tempted to call it, the tortilla, but as you can tell by our naming policy and docs, we are serious people.

Bean definitions

There are two types of bean definitions that can be passed to the contextFactory to be resolved into beans:

  • Hints. (A function or class). ConanJs will understand that your are hinting that an object using the class or the function needs to be created, and it will create it for you.

  • Values. (An object). ConanJs will take the provided value as the bean value. This is handy if you want to put a straight value on the context, or because you want this value to be resolved as the correct dependency for a hint.

When passing hints to the DiContextFactory.createContext, ConanJs will look for any unresolved dependencies for the hint, and will resolve them recursively by name (this is explained in details on the section below).

Resolving Dependencies

Resolving each bean definition into a bean is the process called Resolving Dependencies.

Resolving values is straight forward, the value for the bean is exactly the same value as provided to the DiContextFactory.

Resolving hints is more complex, when resolving hints there are three scenarios based on the underlying function or constructor parameters.

  • No parameters: The function/constructor is invoked, the return value is the bean.

  • With parameters: If the hint has parameters, the resolution by name starts where each parameter is resolved and when all of them are resolved, the function/constructor is invoked with the parameters, the return value is the bean.

Resolution by name

Resolution by name is the process by which a hint needs its parameters resolved.

ConanJs will obtain the name of the parameter, and resolve it agains the context.

This is where the recursive nature comes into play, basically there are two scenarios when a parameter needs to be resolved

  • if there is a value with the same name provided in the context, is resolved to that value

  • If there is a hint with the same name provided in the context, it recursively resolve it.

If you are familiar with DI you might be aware of the different flavours of resolutions, for instance type resolution...

ConanJs being JS based, and JS not having types means that we can only support name resolution.

Aux bean definitions

Lastly, is important to understand that you can optionally pass a second set of key value pairs of bean definitions to DiContextFactory.create, these are called Aux beans definitions.

If you do this, ConanJs will combine both bean definitions for the purpose of resolving any bean, just as if you have passed them together, the difference is that the context will not expose the resolved beans for the Aux beans definition.

This is handy, as many times you will have bean definitions passed to only satisfy internal implementation details, and it doesn't make sense to have their resolved beans available in the context.

Understanding the purpose of many of the concepts exposed here is difficult without real use cases, please bear while we take you through Creating the Context and Using the Context as things should start making sense soon!