dolittle / vanir

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Decouple from Tsyringe as IoC container

einari opened this issue · comments

As a developer I'd like to have the possibility to chose my own IoC container and not be locked into Tsyringe as the only IoC container.

Design Thoughts

In order to enable this, we need to be able to collect or forward the collection of metadata typically done using @decorators. The reflection metadata is typically used by the IoC container to know how to satisfy dependencies through their type representation; a token to bind towards.

By introducing our own decorator we could forward to the IoC containers decorator. Within Vanir we'd typically consistently use our own decorator, while outside of Vanir in a solution - the developer could chose to use the Vanir decorator or the decorator from the IoC chosen for the project.

Proposed implementation

In the package in Vanir called dependency-inversion we could add a decorator called:

@injectable

Even though this is the same as Tsyringe has called it, we can still do the same. This seems to be used by other containers as well (e.g. Inversify).

Typically this would be used as follows:

@injectable
export class MySystem
{
    constructor(private readonly someDependency: SomeOtherSystem) {}
}

In addition to this we'd need a way to register bindings. We have today an interface called IContainer and an implementation Container. Suggest we expand the interface with binding capabilities.

Example APIs:

// Interface - represented as abstract class to provide runtime representation of it
abstract class IMySystem {}

// Class that implements the abstract class
class MySystem implements IMySystem {}

// Standard binding - type to type (leverage Constructor<T> found in @dolittle/rudiments
container.bind(IMySystem).to(MySystem);

// Scoped to singleton (one instance):
container.bind(IMySystem).to(MySystem).singleton();

// Bound to instance:
container.bind(IMySystem).to(new MySystem);

The bind interface would be a fluent API.
For inspiration, we have an interface in C# we used to have:
IBindingProviderBuilder

which returns:
IBindingBuilder

which then returns:
IBindingScopeBuilder.

I think the methods on the first interface could be part of the IContainer interface.

Suggest we also then separate out Tsyringe into a new package: @dolittle/vanir-dependency-inversion/tsyringe.

Once we have it separated out, we would need a way to configure which container to use for both the backend parts - Host and frontend parts - Bindings in Web and Bootstrapper for React.

In addition to this, we'd need to change all calls directly to Tsyringe with our own container.
Suggest there is a global container object (of type IContainer) available that we can work with and it would be the first thing that gets set with the correct implementation (Tsyringe, Aurelia...)