Dependency Inversion Principle (DIP) with Spring Framework

khalil la - Feb 26 - - Dev Community

The Dependency Inversion Principle (DIP) is one of the SOLID principles that promotes loosely coupled code by ensuring high-level modules do not depend on low-level modules but instead rely on abstractions.

DIP in the Spring Framework

Spring Framework naturally supports DIP through Dependency Injection (DI) and Inversion of Control (IoC). These mechanisms help reduce direct dependencies on concrete implementations, making the code more flexible and testable.


Implementing DIP in Spring

1. Without DIP (Tightly Coupled Code)

class EmailService {
    public void sendEmail(String message) {
        System.out.println("Email sent: " + message);
    }
}

class NotificationService {
    private EmailService emailService = new EmailService(); // Tightly coupled

    public void notifyUser(String message) {
        emailService.sendEmail(message);
    }
}
Enter fullscreen mode Exit fullscreen mode

Here, NotificationService directly depends on EmailService, violating DIP.


2. Applying DIP with Spring (Loosely Coupled Code)

We introduce an interface and use Spring’s Dependency Injection to manage dependencies.

// Step 1: Define an abstraction (interface)
public interface MessageService {
    void sendMessage(String message);
}

// Step 2: Provide concrete implementations
@Component
class EmailService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Email sent: " + message);
    }
}

@Component
class SMSService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("SMS sent: " + message);
    }
}

// Step 3: Use dependency injection in high-level module
@Component
class NotificationService {
    private final MessageService messageService;

    @Autowired // Spring injects the appropriate bean
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notifyUser(String message) {
        messageService.sendMessage(message);
    }
}

// Step 4: Define a Spring Boot application to test
@SpringBootApplication
public class DipApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DipApplication.class, args);
        NotificationService notificationService = context.getBean(NotificationService.class);
        notificationService.notifyUser("Hello, DIP with Spring!");
    }
}
Enter fullscreen mode Exit fullscreen mode

How DIP is Achieved in Spring?

  1. Abstraction: MessageService defines a contract without depending on a specific implementation.
  2. Dependency Injection: Spring injects MessageService dynamically via constructor injection.
  3. Loose Coupling: NotificationService depends on MessageService (an abstraction) rather than a concrete class.

Benefits of Using DIP in Spring

Flexibility: Easy to switch implementations (Email, SMS, Push Notifications, etc.).

Testability: Enables mocking of dependencies for unit tests.

Maintainability: Reduces tight coupling, making code easier to extend.

By following DIP in Spring, your application becomes more modular, testable, and scalable. 🚀

. . . . . . . . . . . . .