Service Container
@fusion.io/container
Last updated
Was this helpful?
@fusion.io/container
Last updated
Was this helpful?
Fusion Framework was designed with Dependency Injection (DI) in mind. To archive a seamlessly DI, the heart of Fusion Framework is a service container.
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 .
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.
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:
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:
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.
The Service Container of Fusion Framework was created to address this problem.
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
To bind a service into Service Container, we'll use the .bind()
method:
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:
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:
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.
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.
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()
:
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:
So instead of doing this again and again, we support an autoBind()
method. The above example can be equivalent to:
Similar to autoBind()
, we support autoSingleton()
.
We also support bind()
, singleton()
functions as ES7 decorators, so you can shorten the above code to this:
First we'll create an Abstract key which the value was SomeService
We'll create MyService
as the Concrete service and bind it into the container with the abstract key SomeService
Now, we instruct the Service Container that MyService
is a concrete of the key SomeService
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
.
Now, in the main.js we can have the final result like this:
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:
When using the .inject()
decorator, the dependency you specified will be passed to your class method as the last parameters.
That's all you need to know about DI with @fusion.io/container for now 👍
Fusion Service Container was inspired by .
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 . Here is a demonstration about it: