Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hot Topic: Dependency Injection - Let's Talk about it

Hot Topic: Dependency Injection - Let's Talk about it

Dependency Injection (DI). By now, everyone should have heard about it.
But what actually is "Dependency Injection" and "Inversion of Control"?
We can find 'it' implemented in a lot of frameworks nowadays and for
someone not familiar with the concept it might seem like magic.
In this session we are going to take a closer look behind the curtains
of this software design principle and show what DI can do for us and how
a very simplistic version could be implemented in Javascript.

Yannick Baron

February 13, 2020
Tweet

More Decks by Yannick Baron

Other Decks in Programming

Transcript

  1. Hot Topic: Dependency Injection Let's Talk about it! Yannick Baron

    https://www.thinktecture.com/yannick-baron
  2. "list dependencies in the constructor and magically have them available"

    • design pattern based on the Inversion of Control principle • different styles of injection (Constructor, Setter, ...) Dependency Injection (DI)
  3. • principle in software design • take control from individual

    class (or function) • give control to application • essentially: making things pluggable → Dependency Injection is a way to achieve Inversion of Control Inversion of Control (IoC)
  4. • transparency: easily determine dependencies of a class • reuse:

    pluggable logic / change behaviour "from the outside" • testing: a whole lot easier Why Dependency Injection?
  5. // Consumers
 Consumer1() {
 new FileLogger();
 new Communicator();
 }
 Consumer2()

    {
 new FileLogger();
 new Communicator();
 new AuthHandler();
 } what do consumers depend on? consumers should not have to know about how to instantiate Service classes no way to replace an implementation (FileLogger vs ConsoleLogger)
  6. // Services
 FileLogger() { new FileWriter(); }
 HttpClient() { }


    AuthHandler() { new HttpClient(); }
 Communicator() { new HttpClient(); } // Consumers
 Consumer1() {
 new FileLogger();
 new Communicator();
 }
 Consumer1() {
 new FileLogger();
 new Communicator();
 new AuthHandler();
 } what do consumers depend on? consumers should not have to know about how to instantiate Service classes no way to replace an implementation (FileLogger vs ConsoleLogger) what if we want the same instance of the HttpClient in multiple classes? (singleton? yikes.) duplication of construction logic
  7. // Services
 const logs = new FileLogger();
 const http =

    new HttpClient();
 const comm = new Communicator(http);
 const auth = new AuthHandler(http);
 
 // Consumers
 new Consumer1(logs, comm);
 new Consumer2(logs, comm, auth); easy to identify dependencies consumer does not know about the intricacies of instantiating a dependency easily replace a dependency (interfaces!) same instance goes to different consumers creation logic once where we bootstrap the app
  8. • collection of creation logic and instances • keeps all

    creation logic in a single place • keeps creation logic out of our classes • can create an instance when it is needed Dependency Injection Containers
  9. • small project with very few classes → no DI

    Container needed • huge projects with multiple entry points do not need instances of all classes
 e.g. API request to single controller (simple dispatcher)
 route definition → instantiates controller → needs dependencies → responds • after setup, simple usage:
 
 const consumer1 = container.get(Consumer1);
 • dependencies created for us but still replaceable Dependency Injection Containers
  10. // Service Locator
 new Consumer1(container)
 new Consumer2(container) // Dependency Injection


    new Consumer1(
 container->get(FileLogger),
 container->get(Communicator),
 )
 new Consumer2(
 container->get(FileLogger),
 container->get(Communicator),
 container->get(AuthHandler),
 )
 Service Locator hides dependencies complicates creation logic couples consumers to container couples the whole app
  11. function getUserRoles(user): Role[] {
 if (!userExists(user) || !isLoggedIn(user)) {
 throw

    Error('The user does not exist or is not logged in');
 }
 ...
 } function getUserPermissions(user): Permission[] {
 if (!userExists(user) || !isLoggedIn(user)) {
 throw Error('The user does not exist or is not logged in');
 }
 ...
 }
  12. function getUserRoles(user: User): Role[] {
 if (!userExists(user) || !isLoggedIn(user)) {


    throw Error('The user does not exist or is not logged in');
 }
 ...
 } function getUserPermissions(user: User): Permission[] {
 if (!userExists(user) || !isLoggedIn(user)) {
 throw Error('The user does not exist or is not logged in');
 }
 ...
 }
  13. • design pattern • function or class that wraps another

    function or class • can be used to reduce code duplication • can be used to extend functionality of the wrapped object / function Decorators
  14. function LoggedInUser(target: any) {
 const user = getUserParam(target);
 if (!userExists(user)

    || !isLoggedIn(user)) {
 throw Error('The user does not exist or is not logged in');
 }
 return target;
 }
 
 @LoggedInUser // Syntactic Sugar
 function getUserRoles(user: User): Role[] {
 ...
 }
 
 @LoggedInUser
 function getUserPermissions(user: User): Permission[] {
 ...
 }

  15. • upcoming spec in JavaScript
 (https://github.com/tc39/proposal-decorators) • member decorators •

    class decorators • built into TypeScript already enableDecoratorSupport Decorators in JavaScript
  16. • construction logic is essentially listing the dependencies • goal:

    get list of constructor parameters • potential solution: turn class into a string and parse for constructor
 caveat: mangling code changes parameter names
 (remember AngularJS and the dependency list of strings?) • problem: JS has no types, type information will be lost Automagic DI with Decorators
  17. • there is a way in TypeScript to write type

    information of decorated classes: emitDecoratorMetadata • how to access said metadata? • JavaScript: part of the decorator proposal • preliminary implementation https://www.npmjs.com/package/reflect-metadata Automagic DI with Decorators in TypeScript