Quando usar o padrão do repositório

20

Li recentemente que não é uma boa prática usar o padrão de repositório em conjunto com um ORM. Pelo meu entendimento, isso ocorre porque a abstração que eles fornecem sobre o banco de dados SQL é muito gotejante para ser contida pelo padrão.

Eu tenho algumas perguntas sobre isso:

  1. O que você faz se deseja trocar os ORMs? Você teria um código específico do ORM em seu aplicativo se não o contivesse em um repositório.

  2. O padrão do repositório ainda é válido quando você não está usando um ORM e você está usando o ADO.NET para acessar dados e preencher os dados do objeto?

  3. Se você usa um ORM, mas não o padrão do repositório, onde mantém as consultas mais usadas? Seria sensato representar cada consulta como uma classe e ter algum tipo de fábrica de consultas para criar instâncias?

user1450877
fonte
1
1) você nunca poderá trocar um ORM devido a seus comportamentos diferentes, mesmo que ambos suportem linq, eles se comportarão de maneira suficientemente diferente para que seu aplicativo seja interrompido. por exemplo, o comportamento de carregamento lento, rastreamento sujo, proxies fantasmas etc .. No entanto, é bom ser capaz de trocar o ORM para uma implementação in-mem para testar ..
Roger Johansson
Por que vale a pena, a minha opinião sobre a discussão Repositório / ORM está aqui: stackoverflow.com/questions/13180501/...
Eric Rei
Onde você leu que é uma má prática?
precisa

Respostas:

3

1) É verdade, mas com que frequência você muda um ORM?
2) Eu diria que sim, porque um contexto ORM é uma espécie de repositório e oculta muito trabalho envolvendo a criação de consultas, recuperação de dados, mapeamento etc. Se você não usa um ORM, essa lógica ainda precisa residir em algum lugar. Não sei se isso se qualificaria como o padrão de repositório no sentido mais estrito da palavra ...
3) Encapsular consultas é algo que vejo frequentemente, mas isso geralmente é mais para fins de teste / stub. Fora isso, eu seria cuidadoso ao reutilizar uma consulta em diferentes partes de um aplicativo, porque você corre o risco de criar uma dependência de algo que pode mudar n-vezes (n sendo o número de locais em que você usa a consulta).

Stefan Billiet
fonte
1
1) Você está fazendo a pergunta errada. Não importa quantas vezes, apenas uma vez. Dado o número de opções ORM disponíveis, pode haver uma que seja melhor do que a que você já está usando. A questão agora é: o que acontece quando você faz? O repositório fornece uma boa abstração. Estive lá e gostaria de ter feito uma abstração em primeiro lugar.
devnull
3
@devnull Eu discordo. Se isso acontecesse no máximo uma vez, consideraria um risco aceitável. Se você tem medo de fazer a escolha errada: experimente mais antes de se comprometer com uma. Em teoria, essa abstração parece boa, mas, na prática, você acaba recriando uma parte bastante grande da API do seu ORM, tudo porque pode acabar escolhendo outra em algum dia. Para mim, isso é esforço desperdiçado, código redundante e mais código que você mesmo precisa manter e garantir. Além disso, alternar um ORM não deve afetar um aplicativo inteiro; Aprenda a colocar limites de domínio.
Stefan Billiet
A alteração do ORM não é a única razão para o repositório. Se você precisar introduzir o cache [distribuído] em seu aplicativo - todas as alterações serão feitas no repositório e o seu BL não será alterado devido a alterações na camada de acesso a dados.
Valery
o cache distribuído não seria incorporado ao ORM na maioria dos casos?
user1450877
Eu acho que depende do ORM. Você pode optar por usar ORM mais leve que o NHibernate ou EF.
Valery
2

1) O que você faz se quiser trocar os ORMs, teria código ORM específico em seu aplicativo se não o contivesse em um repositório.

Eu ainda não estava em uma posição em que a empresa de repente decidiu mudar a tecnologia de acesso a dados. Se isso acontecer, será necessário algum trabalho. Costumo abstrair operações de acesso a dados por meio de interfaces. O repositório é uma maneira de resolver isso.

Eu teria um assembly diferente para implementação concreta da minha camada de acesso a dados. Por exemplo, eu posso ter:

Company.Product.Datae Company.Product.Data.EntityFrameworkmontagens. O primeiro assembly seria usado apenas para interfaces, enquanto outro seria uma implementação concreta da lógica de acesso a dados do Entity Framework.

2) O padrão do repositório ainda é válido quando você não está usando um ORM e você está usando o ADO.net para acessar dados e preencher os dados de objetos?

Eu acho que cabe a você decidir qual padrão é válido ou não. Eu usei um padrão de repositório na camada de apresentação. Uma coisa a ter em mente é que as pessoas gostam de lançar responsabilidades nos repositórios. Antes que você perceba, sua classe de repositório estará dançando, cantando e fazendo todo tipo de coisa. Você quer evitar isso.

Eu vi uma classe de repositório que começou com as responsabilidades GetAll, GetById, Update e Delete, o que é bom. Quando o projeto foi concluído, essa mesma classe tinha dezenas de métodos (responsabilidades) que nunca deveriam estar lá. Por exemplo, GetByForename, GetBySurname, UpdateWithExclusions e todos os tipos de coisas malucas.

É aqui que consultas e comandos entram em jogo.

3) Se você usa um ORM, mas não o padrão do repositório, onde mantém as consultas mais usadas. Seria sensato representar cada consulta como uma classe e ter algum tipo de fábrica de consultas para criar instâncias?

Eu acho que é uma boa idéia usar consultas e comandos em vez de repositórios. Eu faço o seguinte:

  • Defina a interface para uma consulta. Isso o ajudará a testar a unidade. Por exemplopublic interface IGetProductsByCategoryQuery { ... }

  • Defina a implementação concreta de uma consulta. Você poderá injetá-las através da estrutura de IoC de sua escolha. Por exemplopublic class GetProductsByCategoryQuery : IGetProductsByCategoryQuery

Agora, em vez de poluir o repositório com dezenas de responsabilidades, simplesmente agrupo minhas consultas em espaços para nome. Por exemplo, uma interface para a consulta acima pode residir: Company.SolutionName.Products.Queriese a implementação pode residir emCompany.SolutionName.Products.Queries.Implementation

Quando se trata de atualizar ou remover dados, eu uso o padrão de comando da mesma maneira.

Alguns podem discordar e dizer que antes que o projeto seja concluído, você terá dezenas de classes e espaços para nome. Sim você irá. Na minha opinião, é uma coisa boa, pois você pode navegar pela solução no IDE de sua escolha e ver instantaneamente que tipo de responsabilidades um componente possui. Se você decidiu usar um padrão de repositório, precisará procurar dentro de cada classe de repositório tentando definir suas responsabilidades.

CodeART
fonte
Eu gosto da idéia de ter comandos em vez de funções genéricas. Onde posso ler mais sobre como implementá-los no contexto de acesso a dados?
ankush981
1

AVISO LEGAL: O que se segue é baseado no meu entendimento e breve experiência do referido padrão (que é o repositório). Eu posso estar fazendo errado ... na verdade, tenho certeza de que estou fazendo errado :). Portanto, embora seja uma tentativa de resposta, também é uma pergunta disfarçada.

Estou usando o padrão Repository para abstrair a camada de acesso a dados, que na maioria dos casos é um ORM. É uma interface genérica, com métodos para criar, ler, atualizar, excluir e implementar classes para LINQ to SQL e EF até agora. Eu poderia fazer uma implementação que grava em arquivos XML no disco (apenas um exemplo selvagem do que eu poderia fazer com ele). Não estou implementando a Unidade de trabalho, pois ela é suportada pelos ORMs. Se necessário, eu provavelmente poderia implementá-lo. Eu gosto dessa maneira porque, até agora, me proporcionou uma boa separação. Não estou dizendo que não há alternativas melhores.

Para responder suas perguntas:

  1. Quer você goste ou não, a mudança ocorrerá. E se você escreveu vários aplicativos e chegou a hora de mantê-los, mas sente que é uma dor trabalhar com o ORM atual e desejar alterá-lo, você se elogiará por fazer essa abstração. Basta usar qualquer coisa com a qual você se sinta confortável, seja Repositório ou outro padrão / conceito.
  2. Sim, desde que você o utilize para separar o acesso a dados. Como mencionei anteriormente, você pode fazer uma implementação que grave dados em arquivos simples.
  3. Eu uso o Repositório para manter minhas consultas e Objetos de Consulta comumente usados quando tiver consultas que eu queira ajustar (que não são muitas).

Para dar outro exemplo, os funcionários da Umbraco abstraíram o contêiner de DI, para o caso de quererem mudar para outra coisa.

devnull
fonte
0

Se o ORM oferece flexibilidade de LINQ, carregamento ansioso etc., não o ocultarei atrás de nenhuma camada extra.
Se envolver sql bruto (micro ORM), o "método por consulta" deve ser usado de qualquer maneira para obter reutilização, tornando assim o padrão do repositório um bom ajuste.

What do you do if you want to switch out ORMs? 
You would have ORM specific code in your application if you do not contain it in a repository.

Por que você precisaria mudar?
O que possui a maioria dos recursos de que você precisa deve ser usado. É possível que um novo lançamento do ormX traga novos recursos e seja melhor que o atual, mas ...
Se você optar por ocultar o orm, poderá usar apenas os recursos que todos os candidatos têm em comum.
Por exemplo, você não pode usar Dictionary<string, Entity>propriedades em suas entidades porque o ormY não pode lidar com elas.

Supondo que o LINQ seja usado, a maioria dos comutadores orm está apenas alternando as referências da biblioteca e substituindo session.Query<Foo>()por context.Foosou similar até compilar. Tarefa chata, mas leva menos tempo do que codificar a camada de abstração.

Is the repository pattern still valid when not using an ORM and you are using ADO.NET for data access and populating object data yourself?

Sim. O código deve ser reutilizável e isso significa colocar a construção do sql, a materialização do objeto etc. em um só lugar (classe separada). Você também pode chamar a classe "XRepository" e extrair uma interface dela.

If you use an ORM but not the repository pattern where do you keep commonly used queries? 
Would it be wise to represent each query as a class and have some sort of query factory to create instances?

Supondo que o LINQ seja usado, um wrapper de classe seria um exagero no IMHO. Uma maneira melhor seria métodos de extensão

public static IQueryable<T> Published<T>(this IQueryable<T> source) where T : Page
{
    // at some point someone is going to forget to check that status
    // so it makes sense to extract this thing
    return source.Where(x => x.Status == Status.Published && x.PublishTime <= DateTime.UtcNow);
}

Qualquer código usado em vários locais (ou com potencial) e bastante complicado (propenso a erros) deve ser extraído para um local centralizado.

Mike Koder
fonte