DEV Community

loading...
Cover image for Dependency Injection with Doug the Goldfish 🐠

Dependency Injection with Doug the Goldfish 🐠

codalreef profile image Oranda ・Updated on ・3 min read

There are lots of great strategies to keep code manageable and extensible. Today, let's learn about "Dependency Injection".

Dependency Injection

Imagine you're a goldfish named Doug (🐠), and you love bubbles. So much so, that you bought a Bubble Machine with a programmable Typescript SDK.

You write a program to make bubbles when you wake up:

import { Bubbler } from 'bubbler';
const initBubbler = () => {

  //  Instantiate
  const bubbler = new Bubbler({ id: "dougs-bubbler" });

  //  Start the Bubbler
  bubbler.bubble({ startTime: "7:00AM", endTime: "8:00AM" })
}
initBubbler();
Enter fullscreen mode Exit fullscreen mode

Great, now you awaken to fresh, well-oxygenated water πŸ’¦

You tell your friend Mary (🐟), and she's so excited, she buys a bubbler too.

You update the code to initialize both bubblers:

import { Bubbler } from 'bubbler';

const initDougsBubbler = () => {
  const bubbler = new Bubbler({ id: "dougs-bubbler" });
  bubbler.bubble({ startTime: "7:00AM", endTime: "8:00AM" })
}

const initMarysBubbler = () => {
  const bubbler = new Bubbler({ id: "marys-bubbler" });
  bubbler.bubble({ startTime: "7:00AM", endTime: "8:00AM" })
}

initDougsBubbler();
initMarysBubbler();
Enter fullscreen mode Exit fullscreen mode

It works, but there's something fishy going on here...

Instead of duplicating the initBubbler function, you could have "hoisted" the instantiation step outside the function:

import { Bubbler } from 'bubbler';

const dougsBubbler = new Bubbler({ id: "dougs-bubbler" });
const marysBubbler = new Bubbler({ id: "marys-bubbler" });

const initBubbler = (bubbler) => { 
  bubbler.bubble({ startTime: "7:00AM", endTime: "8:00AM" })
}

initBubbler(dougsBubbler);
initBubbler(marysBubbler);
Enter fullscreen mode Exit fullscreen mode

Now, we only need the single initBubbler function, even if your friends Larry (πŸ™) and Barry (🐑) decide to buy Bubblers too.

The initBubbler function is no longer responsible for constructing a bubbler instance. Instead, it's injected into the function from the outer scope. This pattern is called "Dependency Injection" (DI).

Dependency Injection is a great way to keep your functions simple, modular, and re-usable by delegating responsibility to the caller.

Inversion of Control

Further, because the "caller" is now responsible for initializing the Bubbler (instead of the initBubbler function), we say control has been "inverted". Dependency Injection is a means by which to achieve "Inversion of Control" (IoC).

IoC Container

The outer scope, responsible for instantiating the bubbler dependency, is called the "Inversion of Control Container" (IoC Container).

DI Frameworks

You can use a "DI Framework" to make things even easier. Instead of manually initializing the dependencies, a DI Framework acts as the IoC Container and does the work for you.

You just tell the framework which dependencies your function needs, and once they're initialized, the framework automatically invokes your function.

Angular and Nest are two popular tools that include DI Frameworks. Both of these helped in the writing of this article and shaping my own understanding of DI:

Plugins

DI Frameworks are great for keeping code organized. However, I like to go one step further and build a module for every "Feature" in my app.

When the DI Framework initializes the "Feature Module", it "installs" itself by invoking dependency methods. It then exports its own API for dependencies to install themselves.

We call call these modules "Plugins", because they inject functionality back into the app.

By building apps as a "Tree of Plugins", feature code is centralized instead of being spread throughout the app.

This makes it easy to mix and match features, build new features, and even open your app for extension by external developers (like Wordpress does).

To learn more about building apps as a tree of Plugins, check out my new package "Halia":

Halia - Extensible TS / JS Dependency Injection Framework

Halia is a simple, lightweight, and extensible DI Framework. It's not tied to a particular backend / frontend technology, and you can customize the framework by installing "Plugins".

Conclusion

We hope your time spent as Doug has helped you see value in the DI Pattern and DI Frameworks.

If you'd like, you can stop imagining you're a goldfish and resume normal human function.

Or, you can imagine you’re a duck and learn how to build Pluggable Apps:

Build Pluggable Apps with Lenny the Duck πŸ¦†

Alt Text

All thoughts and comments are greatly appreciated =)

Cheers,
CR

For more articles like this, follow me on: Github, Dev, Twitter, Reddit

Discussion (6)

pic
Editor guide
Collapse
zarszz profile image
Ganjar Gingin Tahyudin

it seems like i use DI everyday without knowing what is DI actually.

Collapse
codalreef profile image
Oranda Author

I felt the same when I learned about DI. It's such a simple concept, but it's nice to stake it with a label. Thanks for reading!

Collapse
devtony101 profile image
Miguel Manjarres

This was actually a fun way to see DI, nicely done

Collapse
codalreef profile image
Oranda Author

Much appreciated Miguel, thanks for taking a look =)

Collapse
wingatekr profile image
Karissa Wingate

CR- love this, but i think you have a typo in the third code example. You're calling marysBubbler with Doug's bubbler ID. (Extra bubbles for doug!!)

Collapse
codalreef profile image
Oranda Author

Ah, thank you!! I appreciate the feedback =)