DEV Community

Cover image for Movendo a lógica de sua aplicação para Services e Repositories
Tadeu Barbosa
Tadeu Barbosa

Posted on

Movendo a lógica de sua aplicação para Services e Repositories

Você conhece os padrões: Service Pattern e o Repository Pattern? Irei falar um pouco sobre a importância deles no desenvolvimento de um sistema.

Me lembro de quando conheci o PHP lá atrás. Não sabia nada sobre boas práticas, padrões de projeto ou coisas do tipo. Não sabia sequer o que era Orientação a Objetos da maneira correta. Escrevia meus programas todos num mesmo arquivo, e quando conheci as classes só movia tudo para dentro de uma mesma função (:

Existem boas práticas que podemos utilizar no nosso dia a dia! Durante muitos anos, muitas programadoras e programadores experientes, viram que a forma como resolviam certos problemas poderiam ser reutilizados em outros programas e assim conseguiriam resolver também esses outros, quer fossem: criacionais, estruturais ou comportamentais.

Alt Text

MVC

A linguagem que mais utilizo no dia a dia hoje em dia é o PHP. Embora os padrões não sejam específicos para uma linguagem ou outra, vemos bastante a utilização do MVC dentro de frameworks PHP, até mesmo em frameworks próprios.

MVC é um desses padrões de projeto, e a sigla significa: Model, View e Controller (Modelo, Visão e Controlador). Basicamente a Model é responsável pela conexão com o banco de dados, a View é responsável por "imprimir na tela algo para o usuário" e o Controller fica como um "orquestrador" entre os dois. Recebendo chamadas e retornando dados.

Padrão MVC

Basicamente a sua View envia requisições para o Controller, por sua vez o Controller pode ou não acessar uma Model para buscar, atualizar ou remover algo do banco e por fim retornar alguma coisa para a View.

A gente acaba vendo muito código aonde as regras de negócio são colocadas dentro dos Controllers ou das Models. Digamos que estou desenvolvendo um sistema de controle de estoque e o usuário clicou em comprar um produto. Beleza! Acesso o método update do meu ProdutosController, dentro do update eu busco o estoque atual do produto, faço o calculo, verifico se dá ou não para comprar aquele produto, atualizo e retorno para o usuário tudo dentro do método update. Tudo certo até ai, mas digamos que o seu chefe chega até você e te diz o seguinte: "Preciso que você crie uma nova funcionalidade! A partir de agora vamos servir um acesso ao nosso sistema por meio de uma api para alguns parceiros." Nesse momento você tem duas opções: ou duplica o código e cria uma abstração para uma api de qualquer jeito, ou cria a coisa do jeito certo e ainda cria testes para garantir que não vai quebrar nada.

Services

A ideia dos Services é separar as regras de negócio, regras da aplicação e regras de apresentação para que as mesmas possam ser facilmente testadas e reutilizadas em outras partes do seu programa.

Há duas maneiras de criar um Service, você pode criar Services mais genéricos responsáveis por todas as atribuições de um Controller. Ou ser ainda mais específico aplicando assim o S do SOLID: Single Responsibility Principle (Princípio da Responsabilidade Única). Em resumo, esse princípio nos diz que uma classe/função/arquivo deve ter apenas uma responsabilidade, pois essa responsabilidade é a razão de mudar. Pensemos sobre o sistema de compras citado a cima, poderíamos ter algumas necessidades como: Cadastrar usuário, Fazer login, Buscar últimos produtos, Buscar produto por id etc. Logo poderíamos ter os seguintes Services: CadastrarUsuarioService, FazerLoginService, BuscarUltimosProdutosService etc.

Repositories

Se os Services são responsáveis pela lógica, os Repositories são responsáveis pela comunicação com os dados que os Services precisam. A classe CadastrarUsuarioService não precisa saber aonde o usuário será cadastrado, precisa apenas saber que ao passar um nome, login e senha para uma classe CadastrarUsuarioRepository, a mesma irá retornar um boleano dizendo se aquele usuário foi ou não cadastrado; e se algum erro foi encontrado no meio do caminho. Um exemplo seria:

<?php

namespace App\Repositories\Usuario;

class CadastrarUsuarioRepository
{
  private $name;
  private $email;
  private $password;

  public function __construct(
    string $name, string $email, string $password
  ) {
    // validação etc
  }

  public function execute(): bool
  {
    $conn = Database::getConnection();
    $stmt = $conn->prepare("
      INSERT INTO usuarios (name, login, password) 
      VALUES (:name, :login, :password)
    ");
    $stmt->execute([...dados]);
    return $stmt->rowCount() === 1;
  }
}
Enter fullscreen mode Exit fullscreen mode

Se lembra que falei anteriormente sobre testar o código? Pois bem, poderíamos utilizar da Injeção de Dependência ou outro padrão, e ao invés de criar diretamente o Repository, criar uma interface e com isso duas classes de Repository: CadastrarUsuarioPdoRepository e CadastrarUsuarioMemoriaRepository.

<?php

namespace App\Repositories\Usuario;

interface CadastrarUsuarioRepository
{
  public function __construct(
    string $name, string $email, string $password
  );

  public function execute(): bool;
}
Enter fullscreen mode Exit fullscreen mode
<?php

namespace App\Repositories\Usuario;

class CadastrarUsuarioPdoRepository 
      implements CadastrarUsuarioRepository
{
  private $name;
  private $email;
  private $password;

  public function __construct(
    string $name, string $email, string $password
  ) {
    // validação etc
  }

  public function execute(): bool
  {
    $conn = Database::getConnection();
    $stmt = $conn->prepare("
        INSERT INTO usuarios (name, login, password) 
        VALUES (:name, :login, :password)
    ");
    $stmt->execute();
    return $stmt->rowCount() === 1;
  }
}
Enter fullscreen mode Exit fullscreen mode
<?php

namespace App\Repositories\Usuario;

class CadastrarUsuarioMemoriaRepository 
      implements CadastrarUsuarioRepository
{
  private $name;
  private $email;
  private $password;

  public function __construct(
    string $name, string $email, string $password
  ) {
    // validação etc
  }

  public function execute(): bool
  {
    // suponhamos que queira retornar verdadeiro 
    // somente caso o email seja: teste@email.com
    return $this->email === 'teste@email.com';
  }
}
Enter fullscreen mode Exit fullscreen mode

Claro, algumas coisas aqui poderiam ser alteradas para termos classes mais independentes. Por exemplo, o nosso arquivo CadastrarUsuarioPdoRepository conhece a classe Database e sabe como fazer uma chamada ao banco. Nesse caso poríamos resolver também com injeção de dependência, criando uma interface e a CadastrarUsuarioPdoRepository conheceria somente essa interface, com métodos como: insert(array $data), por exemplo.

Agora, ao invés de a lógica de sua aplicação estar toda concentrada dentro do Controller ou Model, estará dentro de Services. E no caso de conexão com banco de dados, api ou serviços externos, os Repositories ficarão responsáveis. Com isso reutilizar services e repositories se tornara algo fácil de se fazer, e ainda tem a facilidade de se testar a aplicação!


Bem, em resumo é isso! Espero em breve falar um pouco mais sobre eles.

Espero que esse post tenha sido útil!

Discussion (0)