Skip to content

Dependency Injection

About dependency injection

In general we suggest using dependency injection pattern - this pattern means nothing more than if your object requires collaboration with others, then expect the user (or client) of your object to set this dependencies from outside.

To use this pattern you don't need a separate dependency injection container. But using this approach gives you higher testability and often leads to a cleaner and more flexible architecture. Typical "things" that can be injected are services, repositories or factories. If your object just expects a certain "interface" the user/client of your object can decide what concrete object it wants your object to use.

It might sound like a "hen <-> egg" problem - because someone has to decide on the concrete instance that should be injected.

So somewhere it need to start - and someone needs to inject the correct dependencies to your object - right?

This can be for example: * the orchestration logic (normally in the application layer) deciding which instance(s) to inject. You can achieve this without any framework support. * a dependency registration concept - where you allow also other packages to influence which object should be injected. This normally requires a dependency injection container in the framework.

DI container in Flamingo

Flamingo Framework uses a DI container called Dingo.

The container acts as kind of registry for services (objects of any type), factories and parameters. The container can then return (or resolve) objects and can inject dependencies automatically with some magic involved.

It is mainly used in the core for:

  • managing different contexts and stateful objects (like routing) in the contexts
  • registering ports and adapters

When to use the dependency injection container in Flamingo

  • It is ok to not use the dependency injection container. In fact, overusing the container adds unnecessary complexity. When writing a package you should think of being able to also use it without the container So it is ok to:

    • explicitly initialize your object yourself and decide in the application layer what to inject (if you use dependency injection)
    • explicitly use your own factory directly
  • However it makes sense to use dingo when:

    • you want to use an existing type that already uses dingo for DI
    • for every object which has a state that is depending on the running configuration-context, e.g. in a project where multiple configuration-contexts exist. (because every configuration-context has its own initialized DI container and this DI container takes care of giving you the correct initialized instance.)
    • when you want some configuration from the yml files to be injected
    • when you want to provide "others" (e.g. other modules) with Hooks
    • when you want to properly use the ports and adapters pattern, to register interface (akk secondary ports) implementations

Read more

There is a full documentation of Dingo features in the dingo module: