Criando Repositórios Genéricos com C#
Padrão de Repositório é um dos padrões comuns de design que permitem estruturar seu código de uma maneira que facilitaria o processo de leitura e escrita de dados para o Banco de Dados. O padrão do repositório destina-se a criar uma camada de abstração entre a camada de acesso de dados e a camada lógica de negócios de um aplicativo. Ele oferece muito menos acoplamento especialmente quando as configurações do Banco de Dados mudam.
Vou demonstrar aqui a implementação de um Repositório Genérico e onde usar essa implementação, além de destacar seus benefícios para a estruturação da sua aplicação.
Para quem perdeu o artigo anterior onde expliquei as interfaces genéricas, segue aqui o link.
Agora, imagine você tem muitas entidades, que você precisa para modelar uma camada DataAccess para cada classe !
Primeiro, você precisará fazer uma interface de cada Repositório e, em seguida, uma classe que implementará esses métodos (O ideal é que a operação CRUD ao lado de métodos personalizados de lógica de negócios/dados por entidade). É aí que entra o uso do Repositório Genérico. Ele permite que você siga o princípio DRY (Não se repita) onde você economiza mais tempo mais, com a reutilização do código.
Para nosso projeto, precisamos lançar mão de um artifício que é o uso de uma classe base para modelar todas as nossas classes do projeto, se chamará “BaseModel” onde teremos apenas o ID e uma propridade booleana para indicar se aquela entidade está ativa ou não. (em projetos que já trabalhei já colocamos datas da inclusão, alteração, usuário que incluiu ou alterou por exemplo, para que todas as entidades tivessem herdada esta rastreabilidade).
Assim, a partir desta classe, criaremos suas “filhas” que serão as entidades do nosso projeto exemplo.
Nosso primeiro passo é criar uma interface que aceite o tipo T (Genérico) e que servirá de base para as demais interfaces de repositório.
Depois criaremos interfaces para as classes correspondentes.
Observem o detalhe mais importante, é que não repetimos os métodos que estão no “IRepositoryBase” uma vez que são herdados, apenas criamos os métodos que não fazem parte da classe base !
Agora implementaremos nossas classes de repositórios, vamos partir da classe “RepositoryBase”, onde iremos criar os métodos que serão herdados pelas classes de repositórios (obs: não fiz a implementação aqui, apenas estou demonstrando como montar as classes).
Agora então é só criar as classes de repositórios respectivas
Prestando atenção nos detalhes demarcados:
- Faremos referência para a classe pai “RepositoryBase” onde os métodos padrões já encontram-se instanciados, com destaque à menção ao tipo de classe que estamos usando para que o repositório o utilize com a especificação: <ArtistModel>.
- Fazemos referência para a interface do repositório “IArtistRepository” onde estarão definidos os métodos que precisamos instanciar.
- como a classe “RepositoryBase” já instanciou os outros métodos, só precisamos realizar a instância do método que não existe na classe “RepositoryBase”, simplificando o nosso código.
Podemos ver que a classe “MusicRepository” também será implementada com a mesma herança agora, mas mudando-se o objeto associado à base e a interface.
Agora temos um Repositório Genérico que nos fornece todos os métodos básicos para a manutenção e o acesso aos dados. Além disso, cada Interface dedicada pode implementar sua própria lógica extra de negócios/dados sem interferir na implementação de outras entidades e, mais importante, um design mais coeso e menos duplicação de código.
Mas e o princípio YAGNI (sempre implemente funcionalidades quando você realmente precisar delas, e nunca quando você prever que vai precisar delas) como fica nesta questão? Pensando que iremos implementar apenas uma vez (na classe Base) e que isto servirá para as demais classes por herança, não vejo como uma violação deste princípio, afinal num software com operações basicamente CRUD, sempre haverá necessidade do uso das funcionalidades básicas, e não estaremos implementando em cada classe filha, somente na base, isto gerará uma economia de código se tivermos múltiplas entidades para criarmos repositórios e não o inverso !
Bom, a parte conceitual está ok, agora o que fazer em cada método que instanciamos na classe base? e as classes filhas, como fazer para que tudo funcione ?
No meu próximo artigo, mostrarei como botar isso para funcionar com Entity Framework Core.