SOLID Principles in JavaScript: What Does the "D" Stand For? by@serhiirubets

SOLID Principles in JavaScript: What Does the "D" Stand For?

"Dependency Inversion Principle" says: high level modules should not depend on low level modules; both should depend on abstractions. "Details should depend upon abstractions," says author. "Single responsibility" principle says: "abstractions don't depend on details" This principle is the last part of the SOLID Principles in JavaScript series in JavaScript. If you haven't read the previous three (first part, second part, third part, fourth part), I highly recommend to read them first and come back.
image
Serhii Rubets HackerNoon profile picture

Serhii Rubets

I'm a Fullstack JS engineer with 7 years of experience. Also, I'm a mentor, teacher, and author of front-end courses.

linkedin social iconinstagram social iconfacebook social icon

Hello, guys, this is the last part of SOLID Principles in JavaScript and if you haven't read the previous three (first partsecond part, third part, fourth part), I highly recommend to read them first and come back here.

"D" - Dependency Inversion Principle. This principle says: high level modules should not depend on low level modules; both should depend on abstractions. Abstractions should not depend on details.  Details should depend upon abstractions.

As usual, this description could be unclear, so let's try to understand this principle with examples.

Let's imagine we want to create logic for working with movie data. Let's create a simple Movie class:

image

Later we understand that we want to save information about video in local storage and we, following the single responsibility principle c will create another class for this purpose:

image

Everything is good, and our logic is used by many files.

image

Then, we want to save not to local storage, but to the file system. No problems, let's create a class for this logic:

image

And now, we need to changes places from local storage, to file storage:

image

Now, it seems not a problem, just 4 lines need to be deleted and replaced by another. But, as we discussed early, imagine if you used local storage methods many times, in many files. It will be really hard to find all places and change them properly. By the way, if you wrote tests for this, all tests also should be changed.

Ok, we changed and now we can continue working. Our system becomes bigger and bigger, and later, we decide to change our logic from file system to DB storage, like mongo DB or SQL, what we will do? Following the"Single responsibility principle", we will create another class for DB.

image

And now, we faced the same problem, we should go across all files, places, where our previous logic with file storage and change it for new, for DB logic. We should change the methods' name, signature, in all places, in all files. And of course, if we have tests, we should fix them.

image

The more places with our previous logic, the harder it is for us to change them. And of course, it could be a cause of bugs in our application.

I really hope you understand the problems, and now, we can see how to avoid these problems. Remember, high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details.  Details should depend upon abstractions?

Let's start refactoring peace by peace. And let's start from abstraction. Let's create a class, "MovieStorage". This class will be our "abstractions". And abstractions should not depend on details. How can we achieve it? Simple. We create methods for our "MovieStorage" class, which will be used instead of MovieDBStorage, MovieFileStorage.

image

This class will be like an interface. All our code will use these methods, which will not change their names. It means that our high-level modules (places, where our "abstraction" uses) will not depend on our internal logic.

Next, we will create separate classes for each store, as we did before, but for this time all of them will be using the same names, params (signature) as our "abstract" class:

image

The last step, adapt our "abstraction":

image

Now, our "abstraction" doesn't depend on details. MovieStorage receives an instance of any storage, that follows our interface. Then, we can use it:

image

And if sometime later we decide to change from file storage to cache storage, local/session storage, mongoDB, sql etc. we just should prepare a new specific storage class (for mongo, redis, sql), which should implement methods with the exact name as in "abstraction" and pass the instance in the constructor:

image

We changed just param from

new MovieStorage(new MovieFileStorage())
to
new MovieStorage(new MovieDBStorage())
and that's all. We don't need to go through all files and change them. We don't need to change our current tests. All our current files will be using the same abstraction. And our abstraction doesn't depend on logic, inside it.

This is the end of our "SOLID" in JS article series and I really hope you enjoyed them and you can use at least one of them in your practice.

You can use all of them or you can choose just one, for example, single responsibility principle, and go through your code, try to investigate, do all places follow this principle. If not, you can refactor your code.

Next, you can take the "Dependency Inversion Principle" and check your code for this principle. Fortunately, frameworks like "Angular" or "NestJS" follow this principle and if you use these frameworks, you can see them in practice in your project, but if you use something else, you can try to refactor similar places, as we discussed in this article.

Thank you for your time and will meet in the next articles.

react to story with heart
react to story with light
react to story with boat
react to story with money
Serhii Rubets HackerNoon profile picture
by Serhii Rubets @serhiirubets.I'm a Fullstack JS engineer with 7 years of experience. Also, I'm a mentor, teacher, and author of front-end courses.
Read my stories

Related Stories

L O A D I N G
. . . comments & more!