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.
- 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
In other words, the bean is the final object that ConanJs creates for you and that it will be available from the context.
A rare capture of ConanJs providing Beans through the context
The Context is a plain object that contains the beans.
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.
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 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 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.
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.