Dependency Injection
If you've followed the documentation so far, you've already seen ModuleManager dependency injection (DI) in action.
Just to take a step back, the idea is that instead of a class having a direct reference to a dependency, you instead inject the dependency from outside.
At its core, this can be very simple, and generally takes one of three forms:
- pass the dependency into the constructor
- pass the dependency into a method
- set the dependency directly onto a property (or setter)
Ideally, your class would only depend on an interface rather than a concrete implementation. Unfortunately, interfaces don't actually exist in JavaScript (you can fake them in TypeScript, but as they don't exist in the runtime, they lose a lot of their power).
For this reason, ModuleManager doesn't actually rely on language-enforced types; it uses a much simpler
system based on names.
Building on the Configurator type system, module types are simply a name associated with a resolver function.
Interfaces or abstract base classes are represented by the provides module setting, as a synthetic type that
needs to be one of a matching set of implementations. There's no language-level type checking, the resolver
created by ModuleManager simply checks whether a named module assignment matches one of the known named providers.
Initialization
ModuleManager doesn't use approach (1), constructor-based DI. If you've ever used AngularJS (particularly 1.x), you know the
pain of trying to ensure that the constructor arguments are sequenced correctly. The library instead wants to be able to instantiate
modules with a no-argument constructor.
Instead, you have the choice to use either approach (2), injecting dependencies through a method; or (3) directly setting properties.
In (2) as we saw in the modules section you can have dependencies passed as a configuration object into the init lifecycle method.
Alternatively, if the module has inject set, dependencies will be set as per (3), directly onto the module.