Implementing Dependency Inversion Principle (DIP) using Laravel

In my previous SOLID principle blogs, we already have discussed the four principles and this one will discuss the last of the SOLID principles which is Dependency Inversion Principle (DIP)

The dependency Inversion Principle says that High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces).

If you look at the UserService.php in the ISP compliant code in the previous blog you will notice that

1. We have injected two separate interfaces in the constructor of service
2. We implement interface-specific methods related to both interfaces with respective objects

ISP compliant UserService as per previous blog is given below

<?php

namespace App\Http\Services;

use App\Http\Repositories\Contracts\UserRepositoryInterface;
use App\Http\Repositories\Contracts\RoleBasedUserRepositoryInterface;

class UserService {

    protected $userRepository, $roleBasedRepository;

    public function __construct(
        UserRepositoryInterface $objUserRepoInterface,
        RoleBasedUserRepositoryInterface $objRoleBasedUserRepoInterface
    ){

        $this->userRepository = $objUserRepoInterface;
        $this->roleBasedRepository = $objRoleBasedUserRepoInterface;

    }
    public function getAllUsers(){
        return $this->userRepository->getAllUsers();
    }
… 
…
…

    // Role-Based Methods
    public function getUsersByRole($role){
        return $this->roleBasedRepository->getUsersByRole($role);
    }
}

Here we are using two different interfaces that are injected into the constructor and that way, because of provider linking we are internally using two repositories for UserService.

That means UserService is dependent on two repositories. To decouple further and to make our code more flexible we can do thefollowing

Step 1. Create a Repository Manager Interface

<?php

namespace App\Http\Repositories\Contracts;

interface RepositoryManagerInterface {

    public function getUserRepository(): UserRepositoryInterface;
    public function getRoleBasedUserRepository(): RoleBasedUserRepositoryInterface;

}

Step 2: Implement the interface

<?php

namespace App\Http\Repositories;

use App\Http\Repositories\Contracts\RepositoryManagerInterface;
use App\Http\Repositories\Contracts\UserRepositoryInterface;
use App\Http\Repositories\Contracts\RoleBasedUserRepositoryInterface;

class RepositoryManager implements RepositoryManagerInterface {

    protected $userRepository;
    protected $roleBasedRepository;

    public function __construct(
        UserRepositoryInterface $objUR,
        RoleBasedUserRepositoryInterface $objRBR
    ) {
        $this->userRepository = $objUR;
        $this->roleBasedRepository = $objRBR;
    }

    public function getUserRepository(): UserRepositoryInterface {
        return $this->userRepository;
    }

    public function getRoleBasedUserRepository(): RoleBasedUserRepositoryInterface {
        return $this->roleBasedRepository;
    }
}

Step 3: Update UserService to the following. Each method in UserService retrieves the correct repository from the repositoryProvider and then calls the relevant method on that repository.

<?php

namespace App\Http\Services;

use App\Http\Repositories\Contracts\RepositoryManagerInterface;

class UserService {

    protected $repositoryManager;

    public function __construct(RepositoryManagerInterface $objRMI){

        $this->repositoryManager = $objRMI;

    }


    public function getAllUsers(){
        return $this->repositoryManager->getUserRepository()->getAllUsers();
    }

    public function getUserById($id){
        return $this->repositoryManager->getUserRepository()->find($id);
    }

    public function createUser(array $data){
        return $this->repositoryManager->getUserRepository()->create($data);
    }

    public function updateUser($id, array $data){
        $user = $this->repositoryManager->getUserRepository()->find($id);
        return $this->repositoryManager->getUserRepository()->update($user, $data);
    }

    public function deleteUser($id){
        $user = $this->repositoryManager->getUserRepository()->find($id);
        return $this->repositoryManager->getUserRepository()->delete($user);
    }

    // Role-Based Methods
    public function getUsersByRole($role){
        return $this->repositoryManager->getRoleBasedUserRepository()->getUsersByRole($role);
    }

}

Step 4: Update AppServiceProvider

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

use App\Http\Repositories\Contracts\UserRepositoryInterface;
use App\Http\Repositories\UserRepository;

use App\Http\Repositories\Contracts\RoleBasedUserRepositoryInterface;
use App\Http\Repositories\RoleBasedUserRepository;

use App\Http\Repositories\Contracts\RepositoryManagerInterface;
use App\Http\Repositories\RepositoryManager;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void{
        $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
        $this->app->bind(RoleBasedUserRepositoryInterface::class, RoleBasedUserRepository::class);
        $this->app->bind(RepositoryManagerInterface::class, RepositoryManager::class);
    }

}

This gives us a DIP-compliant code that also follows SOLID principles. It is flexible code, where UserService is not connected directly with any repository, but we have RepositoryManagerInterface that handles it. The RepositoryManager acts as a single source for all repository instances, simplifying the dependency management for UserService.

Leave a Comment

Your email address will not be published. Required fields are marked *

Share via
Copy link
Powered by Social Snap