|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: 'DI / IOC with @furystack/inject 💉' |
| 4 | +author: [gallayl] |
| 5 | +tags: ['getting-started', 'inject'] |
| 6 | +image: img/003-getting-started-with-inject-cover.jpg |
| 7 | +date: '2021-06-23T08:58:20.257Z' |
| 8 | +draft: false |
| 9 | +excerpt: Dependency injection and Inversion of control is a common practice that tries to protect you from insanity that would happen when you realize that you can't refactor and test a giant global static app structure. @furystack/inject is a simple but powerful tool that you can use in NodeJs and in the browser. |
| 10 | +--- |
| 11 | + |
| 12 | +## Injectable services |
| 13 | +An _injectable service_ is basically a class, decorated with the `@Injectable()` decorator. If you decorate a class, its injectable options (e.g. lifetime) and constructor argument types will be stored and the injector will be able to instantiate a new instance any time. Constructor arguments should be also _injectable services_ and they will be resolved recursively. Take a look at the following example and you'll get the idea: |
| 14 | + |
| 15 | +```ts |
| 16 | +const injector = new Injector() |
| 17 | +@Injectable() |
| 18 | +class Service1 { |
| 19 | + constructor(public service2: Service2, public service3: Service3) {} |
| 20 | +} |
| 21 | +@Injectable() |
| 22 | +class Service2 { |
| 23 | + public value = 'foo' |
| 24 | +} |
| 25 | +@Injectable() |
| 26 | +class Service3 { |
| 27 | + public value = 'bar' |
| 28 | +} |
| 29 | +expect(injector.getInstance(Service1).service2.value).toBe('foo') |
| 30 | +expect(injector.getInstance(Service1).service2.value).toBe('bar') |
| 31 | +``` |
| 32 | + |
| 33 | +All of the 3 classes are decorated as an injectable service. If you request an instance of 'Service1', the framework will also provide an instance of the two dependencies as well. |
| 34 | + |
| 35 | + |
| 36 | +## Injector |
| 37 | +An `Injector` is basically an _extendable container_ that instantiates services with dependencies and handles their lifecycles. The most used and most important method is the `injector.getInstance(MyServiceClass)` that returns with an instance from a requested service. Injectors are smart enough to handle lifecycles (e.g. "singleton" services will be constructed once per injector). |
| 38 | + |
| 39 | +You can create multiple injectors in your project, they can act as multiple separated "global" containers. |
| 40 | + |
| 41 | +You can also organize injectos in a tree structure in the following way: |
| 42 | + |
| 43 | +```ts |
| 44 | +const childInjector = injector.createChild({ owner: 'myCustomContext' }) |
| 45 | +``` |
| 46 | + |
| 47 | +Creating _child injectors_ can be useful if you want to store contextual data (e.g. a per-http-request context that should be initialized once) |
| 48 | + |
| 49 | +## Lifecycles |
| 50 | +The package defines four types of lifecycle: |
| 51 | + - **Transient** injectables are not cached - if you request an instance, you will get a new one every time. |
| 52 | + - **Scoped** injectables are cached, but only on the current level. If a service has been created in a current injector, the existing instance will be returned. |
| 53 | + - **Singleton** injectables are hoisted to the root injector. If you request a singleton, the injector will check create the instance in it's highest parent - and also returns it from there, if already exists. |
| 54 | + - **Explicit** values are not really injectables - you can call `injector.setExplicitInstance(myServiceInstance)` to set up an instance manually. Just like scoped services, explicit instances will be returned from the current scope only. |
| 55 | + |
| 56 | +## Extension methods |
| 57 | +A simple injector can be easily extended by 3rd party packages with extension methods, just like the FuryStack packages. These extension methods usually provides a _shortcut_ of an instance or sets up a preconfigured explicit instance of a service. You can build clean and nice fluent API-s in that way - you can get the idea from one of the [FuryStack Injector Extensions](https://github.com/furystack/furystack/blob/develop/packages/rest-service/src/injector-extensions.ts) |
0 commit comments