Dependency Injection

Dependency Injection (DI), is an important design pattern. Mandarine has its own DI framework built-in, which is used to increase efficiency, readability, and modularity.

This page describes all the information related to Mandarine's built-in dependency injection (DI) framework.

Concepts

Dependencies are objects that a specific class may need to perform certain operations. Instead of programmatically initialize the needed objects, DI allows classes to ask for dependencies from external sources. These dependencies are also known as Injectables.

In Mandarine, the DI framework can only inject Mandarine recognizable components (See here for more information). Note that not all components are injectable as described in the mentioned article.

Component Type

Injectable

Allows Injection

Yes

Yes

Yes

Yes

  • DI container

    • The DI Container is a global registry that handles the registration & keep all the injectable objects in your application together. When a dependency is needed in a component, Mandarine's internal systems will request the DI container in order to get the needed dependency.

  • DI Factory

    • The DI Factory handles the injection of injectable objects (components) as well as their extraction.

Consider

The injection process will take place at mandarine compile time without human interaction. At the end of mandarine compilation, your application will be ready to use as it will have all the injections requested already injected and ready to be used.

Injection forms

As mentioned before, the DI Factory handles the injection of components. This injection can take place in two different forms

  • By class construction

    • The parameters of the constructor of a component are considered injectable objects & mandarine will try to resolve them

  • Manual Injection (Field injection)

    • The use of the decorator @Inject decorating a field will inject the requested object.

      • Mandarine will know what object is requested by on the declared type

    • Although manual injection is available, it is not recommended as it may make your code larger and slower at typescript compile time.

The @Inject Decorator

Syntax:

@Inject()

The @Inject decorator has two possible targets:

  • Field

  • Method parameter

    • Injection by method parameter is not recommended as it may contradict Single Responsibility Principle (SRP)

The @Injectable Decorator

The injectable decorator handles the creation of a manual component. When @Injectable is called, it will create a manual component inside the DI container and it will be readable for mandarine to resolve at mandarine compile time.

For more information & examples about this, please click here.

Syntax:

@Injectable()

The @Injectable decorator has one possible target:

  • Method

    • This method should return an initialized class that will be the reference to the expected object in dependency injection requests

Example:

import { Service, Inject } from "https://deno.land/x/mandarinets/mod.ts";

@Service()
class Service1 {
    public fooNumber: number = 123;
}

@Service()
class Service2 {
    constructor(private readonly service1: Service1) {
    }

    public getFooNumber(): string {
        return `Your foo number is ${this.service1.fooNumber}`;
    }
}

@Service()
class Service3 {
    
    @Inject()
    private readonly service2: Service2;

    public helloWorld(): string {
        return this.service2.getFooNumber();
    }
}

In the example above, we can see how we are using manual injection on a field (line 20) and we are also using injection by class construction (Line 8). After mandarine compile time, when we call our method helloWorld() which is a member of Service3, we will get "Your foo number is 123"

Last updated