- SOLID – Dependency Inversion Principle
Definition according to Wikipedia:
- High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).
- Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
To understand these statements we should know about:
Abstractions and Concrete implementations
Abstract means a summary of the contents. In computer-programming abstractions hide internal details and only show functionality. We can create abstractions using interfaces or abstract classes.
On the other hand, a concrete implementation refers to a specific implementation of an abstraction.

Here, IRepository represents abstraction whereas DatabaseRepository is a concrete implementation of it.
High-Level Modules
High-Level modules are more general, in a software system, they might include business logic, application logic, user interface components, etc.
Low-Level Modules
Low-Level modules are more specific to implementation, they are designed to perform the functionality of the system.
To understand the Dependency Inversion Principle (DIP), consider a scenario in which a user registers for a system and the system sends an email to the user. In this example, we will assume that the system does not actually send the email, but rather just pretends to do so.
Suppose we have a class EmailService that sends emails and a class UserController that handles user registration. The UserController class needs to send an email to the user when they register, so it has a dependency on the EmailService class.
UserController: High-Level Modules
EmailService: Low-Level Modules
Without applying the dependency inversion principle, the UserController class looks like this:

Here, the UserController class is directly dependent on the EmailService class. If we want to change the way emails are sent (e.g. to use a different email service), we would have to modify the UserController class. This violates the dependency inversion principle, as the low-level EmailService class is depending on the high-level UserController class.
To follow the dependency inversion principle, we create an abstraction for the email service, such as IEmailService interface:

We then update the EmailService class to implement this interface:

Next, we update the UserController class to depend on the IEmailService interface rather than the EmailService class:

Now, the UserController class depends on the abstraction (the IEmailService interface) rather than the concrete implementation (the EmailService class).
This allows us to change the way emails are sent without modifying the UserController class. For example, we could create a new class SmtpEmailService that implements the IEmailService interface and use it instead of the EmailService class:
