Você entende Repository Pattern? Você está certo disso?

Mauricio Rodrigues
LaravelTips
Published in
7 min readJan 26, 2018

--

Você está aplicando o padrão da maneira correta ou está somente seguindo o bonde?

Caí novamente em discussões envolvendo Repository Pattern e o “jeito certo” de implementá-lo, depois de publicar nos grupos que participo um pacote com o “meu jeito” de estruturar projetos em Laravel.

Resolvi escrever essa publicação, por perceber que o tempo fica nublado quando falamos de Repository Pattern. Um outro forte motivo também de estar aqui escrevendo, é que as melhores fontes sobre o assunto estão em inglês. E, pelo que tenho percebido, está rolando uma má interpretação dos artigos.

Antes de continuar, você precisa saber que a intensão dessa publicação é expressar o meu ponto de vista sobre o padrão, além de tentar apresentá-lo de forma mais transparente.

O que é repositório?

Repositório é uma interface de consulta e manipulação ESPECÍFICA (leia-se: abstrata, não!!! Genérica, não!!!) em coleções de dados, independente da sua origem.

Acho que essa é a melhor definição para Repository Pattern e, se ela tivesse sido feita antes de algumas implementações, muito provavelmente, muitos transtornos e mal entendidos seriam evitados.

Digo isso porque o que vejo hoje é que as pessoas não entendem “o porquê” da aplicação do padrão no projeto, chegando a utilizá-lo por moda e, o que é pior, de maneira errada. Além de estarem apoiadas no discurso fraco de que Repository é uma maneira de se precaver da troca futura de ORM.

Observe, o argumento é fraco, mas não irreal, como foi muito bem ressaltado por Shawn McCool neste artigo:

“Any sufficiently designed object-oriented application automatically comes with this type of advantage. A very central concept to object-orientation is encapsulation. You can expose an API and hide implementation”

Em outras palavras, o que ele defende é que esse não deve ser o motivo real para a utilização / aplicação do padrão em seus projetos, já que a troca de ORM não acontece assim… num estalar de dedos… e o encapsulamento está ai pra facilitar essa troca, quando esta necessidade realmente existir.

Quando utilizar Repository Pattern

Na minha opinião, sempre que você quiser deixar as coisas mais organizadas, bem definidas, bem separadas e criar uma barreira (de controle e segurança) entre a aplicação e os seus dados, utilize repositórios. Como eu gosto de fazer isso o tempo todo, eu sempre o aplico nos projetos que participo, ignorando-o somente em casos em realmente não há necessidade e que irão gerar desperdícios (de tempo, esforço, etc).

Repositório é um serviço de domínio ou de aplicação?

Repositório é ser um serviço de domínio, que abstrai a camada de persistência da sua aplicação e atua como API para os serviços de aplicação (controllers, CLI, etc). Ou seja, deve ser a única API de acesso confiável aos objetos de domínio e não deve ser responsável por conexões ao banco, envio de e-mail, etc…

Não confunda Serviço de Domínio com Service Layer

Embora se assemelhem no quesito abstração, uma leve distinção, como mencionado por Marcelo Rodrigues aqui nesse post:

É preciso separar bem o conceito de “Service” enquanto camada de negócio, a famosa Service Layer, do conceito de “Service”, como instância de uma classe no container de dependência.

No DDD existem os padrões Service e Repository. O Service do DDD é levemente diferente do Service Layer. No Service Layer cria-se uma “fachada”, usando o padrão Facade (importante não confundir com as Facades do Laravel), isolando e impedindo acesso direto as regras de negócio. Não é necessário saber como ele implementa regras e acesso a dados (por isso o nome Fachada). Numa situação prática, controller ou qualquer outra camada que precisar resolver qualquer coisa de negócio, conversa com a interface da Service Layer. A chance de ter um serviço inchado e com responsabilidades além da conta é alta com esse padrão, se não bem implementado. Sem contar a redundância quando se usa Repository, onde métodos do repositório são replicados no Service.

No DDD, a coisa é diferente. Repository pode ser acessado independente se ou não dentro de uma Service. Por exemplo, dentro do Controller. Não há violação de regras aqui. Service na verdade só é necessário quando você precisa implementar regras de negócios complexas que envolvem mais de um repositório, outros serviços etc. Nem tudo precisa e deve ser isolado em Services.

Veja abaixo um exemplo do que Marcelo disse. Foi criado um comando que é responsável pelo registro de um novo Médico e o Controller acessa o serviço, ao invés de um método do repositório:

A implementação do serviço CreateNewCommand pode ser vista aqui.

Mas não precisa criar um serviço pra tudo, para que os serviços não se tornem simplesmente “proxies” dos repositórios. No exemplo abaixo, o acesso ao repositório é feito diretamente, ao invés de ser utilizado um Service.

OBS: Não estranhe eu estar chamando meu serviço de command. Ele pode ser command, service, handler ou semelhantes.

Repositórios não são genéricos, são específicos!

O maior problema da aplicação do Repository Pattern no Laravel, é que pacotes e “maus entendimentos” tem direcionado os desenvolvedores à construção de repositórios genéricos, que estendem classes abstratas e executam tarefas comuns.

Se você tem classes com comportamentos genéricos, você está violando o padrão, adicionando mais uma camada na aplicação, perdendo seu tempo e/ou gastando o tempo de alguém.

Observe:

Para os códigos acima, minhas considerações são as seguintes:

  1. Você está violando o padrão, por utilizar métodos genéricos;
  2. Seu repositório está atuando somente como um proxy do ORM, sem oferecer o que o ORM possui. Isso força o repositório a cada vez mais se igualar ao ORM;
  3. Instâncias do Models do Eloquent não devem ser retornadas pela API do repositório. Para coleções, arrays devem ser retornados e, para objetos, Plain Old PHP Objects.

Poderia fazer algumas outras considerações, mas, pra mim, essas são fortes o bastante para classificar o exemplo como um mau exemplo.

Tá… Mas qual seria o jeito certo, então?

É simples! Criar repositórios do jeito certo, é mais fácil que criá-los do jeito errado. Um repositório só precisa, basicamente:

  1. Possuir uma interface bem definida;
  2. Não ser ou estender classes abstrata (mais uma vez, genérico não… hahahaha);
  3. Possuir métodos que dizem claramente o que fazem e refletem regras de negócio da sua aplicação, ao invés de utilizar nomes genéricos ou pouco descritivos (Se você pensou em linguagem ubíqua aqui, você tá entendendo);
  4. Não ser responsável por instanciar os models; e
  5. Não retornar Eloquent Models. Ao invés disso, retornar arrays ou Plain Old PHP Objects que não possuam o poder de auto-salvamento;

Vamos ao exemplo

Primeiramente, você pode criar o model que irá ficar responsável pelo armazenamento dos dados, recebendo-os do repositório e enviando-os para a infraestrutura. Não utilizá-lo é uma questão de escolha. Você pode optar por trabalhar diretamente com o QueryBuilder, ao invés de ter uma instância de UserModel, por exemplo.

Em seguida, criaríamos uma interface do nosso repositório poderia ser semelhante a esta:

Observe que na assinatura do método construtor, ele espera UserModel e os métodos createNewUser e updateUserById recebem como primeiro parâmetro $user do tipo User.

User é o nosso Plain Old PHP Object. Será com sua utilização que criaremos o bloqueio de acesso aos dados do UserModel pelas camadas que estiverem acima do repositório.

Lembre do que mencionei lá em cima, “o repositório deve ser a única camada de acesso aos dados”. Desta forma, garantimos a segurança e o controle do acesso aos dados e, ao mesmo tempo, garantimos também que novos usuários não sejam criados com instâncias de Clientes, por exemplo.

Conheça, então, o usuário que será visto fora do repositório.

Como é a proposta, ele é simples e pouco extensivo:

Por fim, o repositório:

Conclusão

Como você viu, a implementação do Pattern não é uma tarefa difícil, embora pareça trabalhosa. Há casos que alguns desenvolvedores preferem violar um pouco o padrão e construi uma classe abstrata que faça trabalhos os mais repetitivos, como os de CRUD.

Essa é uma licença poética que pode ser permitida, dependendo do nível de maturidade e consciência da equipe. Eu prefiro não fazer; outros preferem criar ou até mesmo utilizar pacotes mais enxutos para tal tarefa; então fica a seu critério.

Espero que você tenha entendido e que essa publicação te ajude na construção dos seus próprios repositórios.

Se existirem dúvidas, deixa um comentário.

Se não for pedir muito, deixa um “clap” aí. Esse é o parâmetro que tenho para medir a qualidade e a relevância do conteúdo.

Referências

Queria deixar registrado aqui o meu agradecimento ao grupo do Laravel Brasil no Facebook e ao grupo do Laravel Brasil no Github. As discussões e a anatomia desses grupos me ajudaram muito não só na construção desta publicação, como também na postura que assumi diante da comunidade. Não consigo mensurar o quanto aprendi, não só fazendo perguntas, mas distribuindo o conhecimento que adquiri. Muito obrigado!

Queria agradecer também ao Paulo Freitas pelo suporte que sempre me deu e por tanta dedicação para construção das respostas bem elaboradas que estão nas referências que listo abaixo. Obrigado, Paulo. Tu é o cara! hahaha

Principais Referências

--

--

Mauricio Rodrigues
LaravelTips

Dev sometimes Ops, Laravel Lover, PHP Evangelist, Guitar Player and Saulo’s Papá