Refactoring: Further discussions on DI

Based on conversation with @thatdeji on Twitter

Ikechi Michael
3 min readMar 14, 2022

I received an interesting question about “Refactoring: TF is Dependency Inversion”, which sparked a conversation that I thought to share that as an article with some paraphrasing to focus on relevant context.

TL;DR;

The conversation was about how to tell where DI is necessary, and we touched on topics like Side Effects and Function Predictability.

So here you said their dependency on other modules should be greatly minimized, how about in situations where you need to break them down into sub modules.

Especially when they’re getting bogus and you need to break them into pieces, like let’s say 2–4, you’ll have to import the sub modules in the main one.

Is that like bad cos’ it’s dependent on them?

Me

Ooh that’s an interesting question!

One goal of dependency inversion is to reduce side effects. And side effects are caused by a Function doing something that is unpredictable, usually by referencing something external to your system.

So if a Function depends on other Function, but neither of them cause side effects, they do not need their dependency relationship, inverted. E.g.

const pi = () => Math.PI;
const areaOfCircle = (radius) => pi() * radius * radius;

We can afford to not abstract pi(), because it is a predictable function. It will always return 3.142, so it causes no side effects.

At no point, will the value of pi() change in the future.

However,

const tomorrow = () => new Date(
Date.now() + 24 * 60 * 60 * 1000
);

Here, the value of Date.now() changes every millisecond, so it definitely causes a side effect cos we cannot predict its value. This function cannot be used in a test as is, cos well, its value cannot be predicted.

So we need to make tomorrow predictable, by abstracting its dependency on the System’s DateTime.

So I would refactortomorrowas:

const tomorrow = ({ now = () => Date.now() } = {}) => new Date(
now() + 24 * 60 * 60 * 1000
);

We’ve made now() into a dependency of tomorrow()and we can pass a predictable value for now()during tests. e.g.

tomorrow({ 
now: () => new Date("2022-01-01")
})

This function is now predictable and causes no side effects.

Function Predictability means that we can accurately tell what a function will return, given a set of arguments. It is popularly known as Function Determinism.

@thatdeji

Okay, so the tomorrow function return value won’t change as long as the now() function is made predictable, because that changing part has been abstracted.

Me

Yes, that is it.

--

--

Ikechi Michael
Ikechi Michael

Written by Ikechi Michael

I’ve learned I don’t know anything. I've also learned that people will pay for what I know. Maybe that's why they never pay.

No responses yet