Como evitar métodos de acesso a dados duplicados que recuperam dados semelhantes?


8

Em quase todos os projetos em que trabalho com uma equipe, o mesmo problema parece surgir. Alguém escreve código de interface do usuário que precisa de dados e escreve um método de acesso a dados:

AssetDto GetAssetById(int assetId)

Uma semana depois, outra pessoa está trabalhando em outra parte do aplicativo e também precisa de um, AssetDtomas agora incluindo 'aprovadores', e escreve o seguinte:

AssetDto GetAssetWithApproversById(int assetId)

Um mês depois, alguém precisa de um ativo, mas agora inclui as 'perguntas' (ou os 'proprietários' ou as 'solicitações em execução', etc):

AssetDto GetAssetWithQuestionsById(int assetId)
AssetDto GetAssetWithOwnersById(int assetId)
AssetDto GetAssetWithRunningRequestsById(int assetId)

E fica ainda pior quando métodos como o GetAssetWithOwnerAndQuestionsByIdcomeçam a aparecer.

Você vê o padrão que emerge: um objeto é anexado a um gráfico de objetos grandes e você precisa de diferentes partes deste gráfico em locais diferentes.

Claro, eu gostaria de evitar um grande número de métodos que fazem quase o mesmo. É simplesmente uma questão de disciplina de equipe ou há algum padrão que eu possa usar para evitar isso? Em alguns casos, pode fazer sentido ter métodos separados, ou seja, obter um ativo com solicitações em execução pode ser caro, por isso não quero incluí-los o tempo todo. Como lidar com esses casos?


1
Você pode usar algo como grails que lida com carregamento lento (gorm via hibernate) de propriedades - quando o acesso é tentado. Dessa forma, você só precisa chamar a = getAssetById(x)e, em seguida, pode chamar a.questions etc. sem precisar carregá-las especificamente, pois o sistema ORM subjacente o carrega para você quando o acesso é tentado.
techfoobar

1
Isso seria possível, mas requer que você mantenha algum contexto do banco de dados aberto durante a consulta. Prefiro não ter esse tipo de conhecimento vazando da camada de acesso a dados. E você tem menos controle sobre as consultas executadas. Mas uma opção muito interessante ...
Ronald Wildenberg

Sim, e acho que o ponto principal de separá-lo nos DTOs. Grails vale para a forma não-DTO de fazer as coisas ..
techfoobar

A execução de consultas abertas a partir de uma única interface exigiria que essa interface fosse uma linguagem de consulta específica do domínio. A resposta aceita é semelhante a isso.
precisa saber é o seguinte

Respostas:


4

Em termos de sintaxe, eu criaria um objeto intermediário de criação de consultas com uma interface fluida:

// all the basic, cheap to query fields
AssetDto a = AssetRetriever(asset_id).fetch() 

// some common expensive fields
AssetDto a = AssetRetriever(asset_id).withOwner().withQuestion().fetch() 

// numerous less common fields may not command dedicated methods
AssetDto a = AssetRetriever(asset_id).withFields("foo", "bar").fetch() 

// Better yet, use an enum and enjoy static checking
AssetDto a = AssetRetriever(asset_id).withFields(F_OWNER, F_QUESTION).fetch() 

Espero que seja óbvio o suficiente para implementar. O único método que realmente tocará o banco de dados é fetch().


Eu realmente gosto desta solução. Ele reforça o que eu quero alcançar com uma sintaxe limpa e compreensível e oferece muitas possibilidades de extensão da interface e otimização de consultas SQL.
Ronald Wildenberg

2

Ao lidar com objetos grandes, isso é realmente comum. Embora a adição de novos métodos aumente o desempenho, diminui significativamente a capacidade de manutenção. E, novamente, você precisa escolher entre os dois.

Eu sugiro que você tenha um método que retorne (não necessariamente o menor) dados comumente usados, outro que retorne o objeto inteiro e provavelmente mais alguns para os recursos mais caros.

Outra abordagem é ter métodos que retornem apenas os campos necessários do objeto, como AssetQuestions GetAssetQuestionsById(int assetId)ou Owners GetAssetOwnersById(int assetId).

Junto com isso, você precisa estabelecer algumas regras para recuperar dados. Por exemplo, se alguém precisar de 5 campos do objeto e houver um método existente retornando 8, o método existente deverá ser usado.


Eu esperava que houvesse algum padrão que eu pudesse aplicar para que o problema não ocorresse, mas acho que sua solução é a melhor. Tudo se resume a mais disciplina e pesquisa quando você escreve um novo código de acesso a dados.
Ronald Wildenberg

Gosto da sua solução que apresenta métodos adicionais para recuperar objetos relacionados. Pode custar um pouco de desempenho, já que você não JUNTA mais os dados no banco de dados, mas, para a manutenção, é provavelmente uma grande melhoria.
Ronald Wildenberg

3
Existe uma solução "louca" que me vem à mente: crie um tipo com um campo booleano para cada parâmetro, defina true para os campos que você deseja recuperar e passe esse objeto como parâmetro))))
superM

Isso me passou pela cabeça também e a única desvantagem é que você deve estar preparado para levar em consideração todas as combinações possíveis no seu código de acesso a dados. Embora você poderia de gravação curso consultas muito eficientes para a maioria dos casos comuns ...
Ronald Wildenberg

Eu acho que seria difícil manter também, mas pode funcionar em alguns casos
SuperM

1

Eu já passei por esse mesmo problema recentemente e adotei a seguinte solução:

Os métodos de acesso a dados devem obter dados apenas de um único recurso (por exemplo, tabela de banco de dados) e, se o processo precisar de objetos relacionados anexados ao objeto principal, deverá chamar o método responsável por esses objetos.

Dessa forma, se você precisar de um ativo com seus aprovadores, deverá criar um método de fachada que junte os objetos.

Exemplo:

public Class AssetFacade {

   public AssetDto getAssetWithQuestionsByAssetId(int assetId) { 

      AssetDto asset = AssetDao.getAssetById(assetId);
      List<QuestionDto> questions = AssetDao.getQuestionsByAssetId(assetId);
      asset.setQuestions(questions);

      return asset;
   };
 }

1
Essa é uma solução possível, mas não gosto que você perca todas as possibilidades de otimizar o acesso a dados através do banco de dados. Ou seja, uma consulta com uma junção em três tabelas pode ser mais rápida que três consultas separadas.
Ronald Wildenberg

Concordo com você, mas não conseguiu encontrar uma solução melhor naquele momento. Que bom que você criou esta pergunta para eu aprender uma maneira melhor também!
Marcioggs # 28/12

0

É simplesmente uma questão de disciplina de equipe ou há algum padrão que eu possa usar para evitar isso?

Sim, é uma questão de algumas diretrizes no padrão de nomeação para a equipe. Você pode definir 4 métodos simples, como GetEntityById (), GetAllEntities (), SetEntity (), DeleteEntityById ().

Além disso, você pode ter dois dto's com a nomeação AssetSimpleDto GetAssetById(assetId)e outro dto detalhado chamado as AssetDto GetAssetDetailById(assetId). O primeiro método e o dto são personalizados para oferecer o mínimo necessário, enquanto o segundo traz todas as informações relacionadas que sua funcionalidade possa precisar.


Infelizmente, o caso não é tão fácil quanto 'simplificar' ou 'simplificar'; portanto, isso realmente não resolve o problema subjacente. Isso provavelmente faria com que todos usassem o método 'get all' porque precisam de uma parte do gráfico de objeto que não é retornada por 'get simple'.
Ronald Wildenberg
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.