# Service Container

*Fusion Framework was designed with Dependency Injection (DI) in mind. To archive a seamlessly DI, the heart of Fusion Framework is a service container.*

## Dependency Injection Introduction

In short, **Dependency Injection** (DI) is a technique whereby passing or *injecting* objects (*the dependencies/services*) to another object via constructor or getter methods. Using DI properly, your application will be easier to test and better code reuse [more about DI](https://martinfowler.com/articles/injection.html).&#x20;

For example, ***a booking service will find a ticket for an user, mark it as booked and send an email to the user about the ticket***.

{% code title="BookingService.js" %}

```javascript
class BookingService {

    constructor(ticketStore, emailService) {
        this.ticketStore  = ticketStore;
        this.emailService = emailService;
    }
    
    async book(user) {
        const foundTicket = await this.ticketStore.find();
                
        await this.ticketStore.booked(foundTicket, user);
        
        await this.emailService.send(ticket, user.getEmail());
    }
}


```

{% endcode %}

In the above example, we have injected 2 services into the `BookService`. `ticketStore` for finding an available ticket, and `emailService` for sending the ticket to the user.

To use this `BookingService`, we have to initialize the `ticketStore` and `emailService` first:

{% code title="main.js" %}

```javascript
const bookingService = new BookingService(
    new TicketStore(), 
    new EmailService()
);

// ...

await bookingService.book(user);

```

{% endcode %}

***The trade-off of DI is the complexity of initialization steps***. Especially when the services are using in a nested form. \
For example, the `ticketStore` might need a `DatabaseConnection` to perform SQL query and `emailService` might need an `SMPT` protocol for sending the email:

{% code title="main.js" %}

```javascript
const bookingService = new BookingService(
    new TicketStore(new DatabaseConnection()), 
    new EmailService(new SMTP())
);

// ...

await bookingService.book(user);

```

{% endcode %}

To create an instance of `BookingService`, we first need to know how to create a `TicketStore` service, which in turn needs to know how to create a `DatabaseConnection` and so on... In real world application, the source code is even more complicated.&#x20;

The Service Container of Fusion Framework was created to address this problem.

{% hint style="info" %}
**Service Container** has another name called **Inversion of Control Container** or **IoC Container**.

Fusion Service Container was inspired by [**Laravel's Service Container**](https://laravel.com/docs/6.0/container#introduction).
{% endhint %}

## Service Container

*A service container is a simple map object for holding and getting dependencies/services.* \
*To use the Service Container, we need to install it from NPM*

```bash
npm install @fusion.io/container
```

### Binding basics

To bind a service into Service Container, we'll use the `.bind()` method:

{% tabs %}
{% tab title="bind-example.js" %}

```javascript
import { container } from '@fusion.io/container';
import HelloService from './HelloService';

container.bind('helloService', () => new HelloService());

```

{% endtab %}

{% tab title="HelloService.js" %}

```javascript
export default class HelloService {
    
    sayHello() {
        return 'Hello World';
    }
}

```

{% endtab %}
{% endtabs %}

The `.bind()` method requires 2 parameters: the dependency key `helloService` and a function returning an instance of the service - we call it as a `Factory Function`.

After we bind a service, we can retrieve it from the container by calling the `.make()` method:

```javascript
const helloService = container.make('helloService');

console.log(helloService.sayHello()); // Hello World
```

We can also call the `.make()` method inside the `Factory Function`. By doing so we can  instruct the container how to make a service with nested dependencies easily:

```javascript
container.bind('fooService', () => new FooService());
container.bind('barService', () => new BarService(container.make('fooService')));
container.bind('fooBarService', () => new FooBarService(container.make('barService')));

const fooBarService = container.make('fooBarService');

```

In the above example, we have a `fooBarService` is using the `barService` as a dependency. And the `barService` is using `fooService` as a dependency for itself.

### Singleton binding

If your service was bounded to the to the container via `.bind()` . Every time we call the `.make()` method, the container will invoke the `Factory Function` again to get your service instance.&#x20;

```javascript
container.bind('fooService', () => new FooService());

const instance1 = container.make('fooService');
const instance2 = container.make('fooService');

console.log(instance1 === instance2); // false

```

Sometimes, we only need one instance of service or a *singleton*, and every time we calling `make()`, we can get back the same instance. We can archive this by using the `singleton()` method instead of `bind()`:

```javascript
container.singleton('fooService', () => new FooService());

const instance1 = container.make('fooService');
const instance2 = container.make('fooService');

console.log(instance1 === instance2); // true

```

### Auto binding

{% hint style="info" %}
Thanks to ES6 Map, the dependency key is not limited to string value, but any value can be used. Even a class

```javascript
class FooService {

}

container.bind(FooService, () => new FooService());

```

{% endhint %}

Most of the time, you may find your factory function is doing nothing but 2 steps: making dependencies and creating a new service instance with those dependencies:

```javascript
class Dependency1 {

}

class Dependency2 {

}

class MyService {
    constructor(dependency1, dependency2) {
        this.dependency1 = dependency1;
        this.dependency2 = dependency2;
    }
    
    // ...
}

container.bind(Dependency1, () => new Dependency1());
container.bind(Dependency2, () => new Dependency2());

// We'll do this most of the time
container.bind(MyService, () => new MyService(
    container.make(Dependency1),
    container.make(Dependency2)
));

```

So instead of doing this again and again, we support an `autoBind()` method. The above example can be equivalent to:

```javascript
class Dependency1 {
    static get dependencies() {
        return [];
    }
}

class Dependency2 {
    static get dependencies() {
        return [];
    }
}

class MyService {
    constructor(dependency1, dependency2) {
        this.dependency1 = dependency1;
        this.dependency2 = dependency2;
    }
    
    static get dependencies() {
        return [Dependency1, Dependency2];
    }
    
    // ...
}

container.autoBind(Dependency1);
container.autoBind(Dependency2);
container.autoBind(MyService);

```

Similar to `autoBind()`, we support `autoSingleton()`.

We also support `bind()`, `singleton()` functions as ES7 decorators, so you can shorten the above code to this:

```javascript
import { bind } from '@fusion.io/container';

@bind()
class Dependency1 {

}

@bind()
class Dependency2 {

}

@bind(Dependency1, Dependency2)
class MyService {
    constructor(dependency1, dependency2) {
        this.dependency1 = dependency1;
        this.dependency2 = dependency2;
    }
    
    // ...
}

```

### Inversion binding

A best practice to approach **DI** is binding a **Concrete** service to the Container with an **Abstract** key. This will help your source code following the [Dependency Inversion Principle](https://martinfowler.com/articles/dipInTheWild.html). Here is a demonstration about it:

First we'll create an **Abstract** key which the value was `SomeService`

{% code title="SomeService.js" %}

```javascript
export default const SomeService = 'SomeService';

```

{% endcode %}

We'll create `MyService` as the **Concrete** service and bind it into the container with the abstract key `SomeService`

{% code title="MyService.js" %}

```javascript
export default class MyService {

}

```

{% endcode %}

Now, we instruct the Service Container that `MyService` is a concrete of the key `SomeService`

{% code title="main.js" %}

```javascript
import SomeService from './SomeService';
import MyService   from './MyService';

container.bind(SomeService, () => new MyService());

// ...

```

{% endcode %}

By doing so, the consumer code which using the service as dependecy does not need to be aware about the **Concrete** service - `MyService`, but the **Abstract** key - `SomeService`.

{% code title="Consumer.js" %}

```javascript
import SomeService from './SomeService';

@bind(SomeService)
export default class Consumer {
    constructor(someService) {
        // Here we'll got MyService instance
        this.someService = someService;
    }
    
    // ...
}

```

{% endcode %}

Now, in the *main.js* we can have the final result like this:

```javascript
import SomeService from './SomeService';
import MyService   from './MyService';
import Consumer    from './Consumer';

container.bind(SomeService, () => new MyService());

const consumer = container.make(Consumer);

// working with the consumer

```

### Method Injection

Sometimes, using constructor injection is overhead. You may find your self using just some dependencies in your class method. We can use the `inject()` decorator for such situation:

```javascript
import { inject } from '@fusion.io/container';

class MyService {
    
    @inject('dependency')
    someMethod(parameter1, parameter2, depdency) {
        //
    }
}

const myService = new Service();

myService.someMehod('foo', 'bar');
```

When using the `.inject()` decorator, the dependency you specified will be passed to your class method as the last parameters.&#x20;

That's all you need to know about DI with @fusion.io/container for now 👍

{% hint style="info" %}
**@fusion.io/container** is having zero dependency and can be used in any javascript project.&#x20;
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://fusion.sphinx.vn/design-concepts/service-container.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
