Dependency Injection in Aurelia: How Does It Work?
When it comes to building a well-structured and maintainable application, it's good practice to break complex objects into smaller ones. This allows each object to be focused on individually, but collaborate with others to create a multipurpose app.
A dependency injection container is a great tool that can help us stitch together smaller objects again at runtime.
What is Dependency Injection?
Dependency is an object that could be used by another object. Dependency Injection (DI) (also known as "Inversion of Control") is a software design pattern, which is used to achieve loose coupling between objects and their dependencies. But, instead of your objects constructing their own dependencies, dependency injection allows you to pass the needed dependencies into the constructor and bypass this problem altogether.
For example, we have an Order
object and it depends on Price, Quantity, Customer
etc. If we do it the traditional way, we would create those dependency objects along with defining an Order
object. Let's look at an example without DI here:
export class Order { constructor() { this.price = new RegularPrice(); this.customer = new Customer(); } //and so on }
But in this case, where we need our Price dependency object to be a DiscountedPrice()
, we would have to recreate the whole Order
object for that. After using dependency injection, Price
and Customer
dependencies are being injected into the Order
object at run time.
export class Order { constructor(price, customer) { this.price = price; this.customer = customer; } }
So, when we have a deal with Dependency Injection and Inversion of Control, we use 'Container'
. Its role is to construct objects (IoC part) and create the dependencies that this object needs by passing those dependencies as constructor parameters (DI part). This way we don't have to instantiate them directly.
Mechanisms in Aurelia framework for DI
Generally speaking, there are two ways of using class dependencies in Aurelia. The first one is with a use of inject
Decorator.
Decorator is a feature of an upcoming ES7 that can take a target, name and property descriptor as arguments. It can be defined for either a class or property.
Let's look at an example here...

First, we need to make dependencies like Price, Customer and Shipping
to be accessible for our Order
class. In order to import those classes, we use import syntax here.
Then, we can make a use of inject
decorator, which executes the code against its target (Order
class) and modifies it to provide the metadata that this class needs. So basically, when the class constructor is being called, Aurelia DI container will go to that module, create its instance and inject the reference of that instance to the constructor.
The other way to use class dependencies in Aurelia is by creating a static inject()
method on your class. That will also do the work for you in case you don't want to use ES2016 features yet.

Conclusion
DI is used a lot in Aurelia and it is a powerful tool that allows the loose coupling of dependent components. By using it, we make our ViewModel more flexible and more testable.
How are you using Dependency Injection in your projects? How has it helped?