question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Dependency Injection enhancement

See original GitHub issue

Issue

Currently Foal resolves dependencies with property injection. Instance are injected with the dependency annotation. This scenario have some issues:

  • Compiler is configured with “strictPropertyInitialization” to false. That’s a huge problem because this configuration defined properties to be optionally initialised by default. It’s better to allow developers to choose that (I personally always initialised my properties in constructor and mark them as readonly for immutability, so I like to have this option activated in order to give me errors if I forget something)
  • Documentation give an example where the property is public. DI based on property injection is ok because everything is public in JavaScript and annotation don’t care about that, but to ensure that the dependency decorator will be the only one to manipulate the property in TypeScript, it will be better to show an example with private & readonly:
@dependency
private readonly logger: Logger
  • Injection in constructor is far way better than property injection because it helps to implement immutability (that’s why developer must have the choice to use “strictPropertyInitialization”). Foal can do both. A quick example on ctor injection:
export class ApiController {
  private readonly service: Service

  constructor(@inject s: Service) {
    this.service = s;
  }

  @Get('/')
  index(ctx) {
    return new HttpResponseOK(this.service.hello());
  }
}
  • Foal can’t integrate third part IoC container. It means that developers can’t use Inversify or tsyringe to manage their dependencies.

Possible solution

This is definitely not easy to implement. Foal can handle both scenarios:

  • Property injection: Keep the current implementation
  • Constructor injection: Need to be done with a Dependency Resolver. This one can be abstract with an interface, that’s allowed third part IoC container to be integrated by implementing a custom resolver. Foal have to provide a default one for this scenario.

I can probably help you on this enhancement because i’m working on a similar feature in my Mediator library.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
spontoreaucommented, Mar 28, 2019

Thank you for the detailed feedback Sylvain. And sorry for the late reply, I wanted to read a bit more on dependency injection and think about it before replying back!

Yeah sure, don’t worry about that 😉

Backward compatibility

Foal can handle both scenarios

  • Property injection: Keep the current implementation

Yes. In any cases, keeping the current property injection is important so that we guarantee backward compatibility. I agree on this point.

Definitely agree with that, Foal can’t remove this feature and some developers prefers property injection!

Currently the setting is set to false because both @dependencies and TypeORM @Column properties are unassigned in the class definition. Enabling this feature is still possible by adding a ts-ignore comment to these properties.

… I forgot that point for TypeORM 😢. So it seams near impossible to switch this configuration yet!

Adding the readonly modifier sounds like a good idea to prevent the dependency from being changed in the middle of the class life. Do you want to submit a PR to update the docs? I think that @dependency is only mentioned in architecture-overview.md, services-and-dependency-injection.md, configuration.md and logging-and-debugging.md.

Yeah sure, I can do that 😉

Regarding the private attribute, I wonder if this wouldn’t be strange in the case of property injection since we set the field from the outside of the class.

I have the same feeling because currently private is just a “TypeScript” concept. Also we don’t know the impact of ECMAScript private fields on TypeScript and Decorators. So it’s better to just mark the property as readonly and that’s all!

Integration of a third-part IoC container

Would this feature (being able to integrate a a third-part IoC container) bring a real value to the framework? Your proposal using the @inject seems to me sufficient to solve the problem you describe (strict property initialization option). It also keeps the framework simple without adding extra abstraction / complexity.

😃

It helps to migrate to Foal. Application that uses Inversify don’t have to be refactor in order to switch to Foal. But it’s definitely not a huge priority.

Default DI

I think that keeping the property injection (with the readonly keyword) as default DI in Foal is a good choice (from a simplicity point of view). This way, developers unfamiliar with DI can easily get started with the framework without further knowledge (progressivity). Moreover, the way it is implemented (only possible with JS/TS) guarantees that:

  • the dependency is initialized;
  • the dependency cannot be changed (as long as we add the readonly keyword);
  • and, during testing, mocks can be provided as dependencies.

For developers that want to use the strictPropertyInitialization configuration (or are more familiar with the constructor injection), the @inject constructor decorator can be an interesting alternative solution that the framework could provide.

I agree!

Implementation

This is definitely not easy to implement.

Yeah 😃.

I remember having issues with inheritance in an old version of the framework using constructor injection.

Supporting both property & constructor injections makes also things harder.

To be honest, it’s a really a complex topic because of the nature of JavaScript.

I’m currently working on OpenAPI/Swagger integration, which will implies some internal changes in the @foal/core package (the way that metadata are managed). I think it’s probably better to wait until these changes come out before adding the constructor injection support.

Let me know if you agree / disagree or want to add something.

This feature is a must have, so I understand that injection support isn’t a priority. It can give me some time to work on my library, and after that I can probably help you to implement injection support in Foal 😉

0reactions
spontoreaucommented, Aug 6, 2019

Ok, I understand!

Also it’s really not an easy feature to add in Foal.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Dependency injection in Android | Android Developers
What is dependency injection? · Constructor Injection. This is the way described above. You pass the dependencies of a class to its constructor....
Read more >
Enhancement (Jakarta Context Dependency Injection API)
2nd phase of build compatible extension execution. Allows transforming annotations. In the following text, the term expected types denotes the set of types ......
Read more >
Dependency injection and programmatic lookup
Dependency injection always occurs when the bean instance is first ... WeldInstance - an enhanced version of jakarta.enterprise.inject.
Read more >
Cataloging dependency injection anti-patterns in software ...
Dependency injection improves software modularity by enabling less coupling among modules by refraining them from being aware of implementation details (i.e., ...
Read more >
Building a Flexible Dependency Injection Library for Swift
Dependency Injection is a programming concept that improves testability and allows for easy decoupling of classes. It achieves this by requiring the ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found