Dependency Inversion Principle
Tags: codequality, agile, beginners, codenewbie
Published: 2021-04-23 00:38:48

The **Dependency Inversion Principle** states that entities must depend on abstractions, not on concretions. High-level modules should not depend on low-level modules. Both should depend on abstraction.
 
>High-level modules should not depend on low-level modules. Both should depend on abstraction.

When we talk about **high-level modules** we are referring to a class that executes an action implementing a tool or library, and when we talk about **low-level modules** we are referring to the tools or libraries that are needed to execute an action.

The principle allows for decoupling, which means to separate, disengage or dissociate something from something else. This helps us by reducing dependency and allowing for easier implementations of other tools in the future.

#Example
Let's imagine that we have a Candy Store and we are developing the checkout process. In the beginning, we only planned to implement [Stripe](https://stripe.com/) as our payments processor. Stripe needs for the amount to be passed on as cents to make the transaction. Our classes will look something like this:
```js
//Checkout.js
class Checkout {
  constructor() {
    this.paymentProcessor = new Stripe('USD');
  }

  makePayment(amount) {
    //Multiplying by 100 to get the cents
    this.paymentProcessor.createTransaction(amount * 100);
  }
}

//Stripe.js
//Custom Stripe implementation that calls the Stripe API
class Stripe {
  constructor(currency) {
    this.currency = currency;
  }

  createTransaction(amount) {
    /*Call the Stripe API methods*/
    console.log(`Payment made for $${amount / 100}`);
  }
}
```
Notice that we created a dependency between our `Checkout` class (high-level module) and `Stripe` (low-level module), violating the Dependency Inversion Principle. The dependency is especially noticeable when we convert the amount to cents. The `Checkout` should not care about which payment processor is being used, it only cares about making a transaction.

To decouple these two modules, we would have to implement an intermediary between the checkout and the payment processor, creating an abstraction so that no matter what payment processor we use, the `Checkout` class will always work with the same method calls. The new `PaymentProcessor` class will be in charge of adapting everything to payment processor to be used (in this case, Stripe). The intermediary class will have the following code:
```js
//PaymentProcessor.js
class PaymentProcessor {
  constructor(processor, currency) {
    this.processor = processor;
    this.currency = currency;
  }

  createPaymentIntent(amount) {
    const amountInCents = amount * 100;
    this.processor.createTransaction(amountInCents);
  }
}
```
As you can see, the `createPaymentIntent` on the `PaymentProcessor` class is converting the amount to cents. And now we refactor the Checkout class to implement the abstraction:
```js
//Checkout.js
class Checkout {
  constructor(paymentProcessor) {
    this.paymentProcessor = paymentProcessor;
  }

  makePayment(amount) {
    this.paymentProcessor.createPaymentIntent(amount);
  }
}
```
Now, if we ever need to change our payment processor, we can do so by passing the new processor instead of Stripe to the `PaymentProcessor` constructor. Then, we pass the `PaymentProcessor` to the `Checkout`: 

```js
//index.js
const paymentProcessor = new PaymentProcessor(new Stripe('USD'), 'USD');
const checkout = new Checkout(paymentProcessor);
```

Imagine that now we are asked to replace Stripe with another payment processor that does not require for the amount to be converted to cents **but** on every transaction asks for the currency that's going to be used. The resulting code will be the following:
```js
//Checkout.js
class Checkout {
  constructor(paymentProcessor) {
    this.paymentProcessor = paymentProcessor;
  }

  makePayment(amount) {
    this.paymentProcessor.createPaymentIntent(amount);
  }
}

//PaymentProcessor.js
class PaymentProcessor {
  constructor(processor, currency) {
    this.processor = processor;
    this.currency = currency;
  }

  createPaymentIntent(amount) {
    this.processor.createTransaction(amount, this.currency);
  }
}

//BetterProcessor.js
class BetterProcessor {
  createTransaction(amount, currency) {
    console.log(`Payment made for ${amount} ${currency}`);
  }
}

//index.js
const paymentProcessor = new PaymentProcessor(new BetterProcessor(), 'USD');
const checkout = new Checkout(paymentProcessor);
```
Notice how we only changed the processor passed to the `PaymentProcessor` class and how the `Checkout` class remained untouched. We adapted the intermediary class `PaymentProcessor` to the processor needs.

We removed the dependency between `Checkout` and the processor used by implementing the intermediary class `PaymentProcessor`, following the Dependency Inversion Principle.