Princípios SOLID: o que são e como aplicá-los no PHP/Laravel (Parte 05 - Inversão de Dependência)

Princípios SOLID: o que são e como aplicá-los no PHP/Laravel (Parte 05 - Inversão de Dependência)

Dando continuidade à série de artigos sobre princípios SOLID, trago hoje o quinto e último dos princípios.

Dependency Inversion Principle

(Princípio da Inversão de Dependência)

Entidades devem depender de abstrações e não de algo concreto. Isso significa que módulos de alto nível não podem depender de módulos de baixo nível, e sim de abstrações.

Como eu citei na parte 04 "abstração é o coração da orientação a objetos. Em PHP, nós conseguimos alcançar um nível de abstração utilizando interfaces e classes abstratas". Ou seja, assim como na parte 04 nós vamos aplicar o nosso exemplo de Employees e Contractor usando interfaces.

Quando trabalhamos com arquitetura em camadas (que no nosso caso é as camadas Controller, Service, Repository e Model) o ideal é que possamos seguir o fluxo da requisição a partir do Controller, e que as camadas mais externas não saibam nada do que acontece nas camadas mais internas.

Por exemplo:

O Controller não pode depender diretamente do Service, assim como o Service não pode depender da camada de Repository.

Mas por quê?

Pois dessa forma essas camadas estariam dependendo de implementações concretas. Ou seja, se eu mudar alguma coisa no meu Service, pode impactar diretamente no Controller e acabar "quebrando" a aplicação. Isso aumenta o acoplamento entre as classes, o que é uma má prática.

E como resolver isso?

Com a inversão de dependência!

Na parte 04, criei 2 interfaces com o objetivo de deixá-las segregadas, cada uma com suas particularidades.

interface UserInterface
{
  public function register(array $attributes, UserRepository $userRepository);
}

interface ContractorInterface
{
  public function calcWorkedHours(array $hours);
}

Para poder fazer a inversão de dependência, eu preciso injetar a interface do Service no Controller. Mas como fazer isso se temos duas interfaces?

A resposta é simples: herança!

Eu vou criar uma interface, e as duas interfaces criadas anteriormente (UserInterface e ContractorInterface) irão estendê-la.

interface ServiceInterface
{
}

interface UserInterface extends ServiceInterface
{
  public function register(array $attributes, UserRepository $userRepository);
}

interface ContractorInterface extends ServiceInterface
{
  public function calcWorkedHours(array $hours);
}

Dessa forma, conseguimos concentrar toda essa camada de abstração em um único ponto.

Agora, vamos ao Controller e vamos injetar essa interface.

class UserRegistrationController extends Controller
{
    protected $userService;

    public function __construct(ServiceInterface $userService)
    {
        $this->userService = $userService;
    }

    // implementação do código
}

Percebam que mesmo o nosso atributo seja nomeado como $userService, ele é do tipo ServiceInterface. E dessa forma evitamos um forte acoplamento entre as camadas concretas, deixando todo serviço a cargo da abstração, que no nosso caso é a ServiceInterface.

Fica aqui o desafio: tente fazer o mesmo processo entre as camadas de Service e Repository. :)

Espero que tenham gostado dessa série de artigos sobre SOLID, e em breve trarei novos conteúdos.

Fui!