Dependencies are everywhere right now and are always a necessary part of developing any app; however, occasionally our code can easily become highly coupled with them and this is extremely risky. As a result, our app will eventually go down sooner than we expect when one of the dependencies goes down for any reason, and to make it worse external code is out of our control which makes it much harder to deal with, another thing to consider when using a lot of dependencies is changing one of them, that could be also hell and needs a lot of work unless we plan for it carefully to manage the dependencies in a way that makes it less dangerous to use them.
There are several approaches to achieve this, including and not limited to "dependency injection" and "dependency inversion", and in this post, we will discuss a strategy related to dependency inversion
The Problem:
Assume that we have a nodejs app and we need to use a library that generates a random number; by googling this, we could find quite a few of them, and we decide to use the library x-random-generator
and we need to do this to generate a random number.
import xRandomGenerator from `x-random-generator`
const randomNumber = xRandomGeneration.generateRandomNumber()
and let's say that this library generates also random IDs and we will use it like this
import xRandomGenerator from `x-random-generator`
const randomId = xRandomGeneration.generateRandomId()
we went ahead and used this in our code, and we used this library in up to X number of files, and everything is working fine.
After a while, the app grows a little bit more, and now we have a new feature that needs to use a random username using only a special set of characters. we went back to our library and after reading its documentation, we can see that we can't use it to generate the random username we need because this library uses a set of characters containing this -
and _
, but those are not allowed in our username so what should we do now?
we went ahead and found a cool library called y-random-generator
that provides this feature as well as random numbers and random IDs; and because we can't use both of them, so we need to replace the old one with this one, and because we have used it in many places, we will have to touch all the files that used the old library, and this is never a good idea specially if the new library have a different interface.
The Solution:
to avoid this problem we will have to hide our dependencies, no part of our app should ever know which library this part of code is coming from, only one component should know that, and we should use it abstract only the methods that we need to use out of the large library we use.
this component will look this
import yRandomGenerator from `x-random-generator`
export const randomGenerator = {
randomNumber: yRandomGenerator.generateRandomNumber,
randomId: yRandomGenerator.generateRandomId,
randomUsername: yRandomGenerator.generateRandomUsername,
}
We may use this new component across our entire application, and whenever we wish to switch to a different library โeven a one that might even have a very different interfaceโ this component can act as an adopter to ensure that the code still functions properly for our application.
import ZRandomGenerator from `z-random-generator`
const zRandomGenerator = new ZRandomGenerator()
const zRandomUsernameGenerator = new ZRandomGenerator('abcdefghijklmnopqrstuvwxyz')
export const randomGenerator = {
randomNumber: zRandomGenerator.numbers().generate,
randomId: zRandomGenerator.generate,
randomUsername: zRandomUsernameGenerator.generate,
}
conclusion:
To assist lessen the coupling to our dependents and to give ourself a little more freedom when we wish to update or change one of them, we should always aim to hide our dependencies under a layer of abstraction. and This even supports the "Single Responsibility Principle".
Top comments (0)