Referenciando valores do banco de dados na lógica de negócios

42

Acho que essa é outra questão sobre codificação e melhores práticas. Digamos que eu tenha uma lista de valores, digamos, fruta, armazenada no banco de dados (ela precisa estar no banco de dados, pois a tabela é usada para outros fins, como relatórios do SSRS), com um ID:

1 Apple 
2 Banana 
3 Grapes

Eu posso apresentá-los ao usuário, ele seleciona um, ele fica armazenado em seu perfil como FavouriteFruit e o ID armazenado em seu registro no banco de dados.

Quando se trata de regras de negócios / lógica de domínio, quais são as recomendações para atribuir lógica a valores específicos. Digamos que se o usuário selecionou Grapes eu quero executar alguma tarefa extra, qual é a melhor maneira de fazer referência ao valor Grapes:

// Hard coded name
if (user.FavouriteFruit.Name == "Grapes")

// Hard coded ID
if (user.FavoriteFruit.ID == 3) // Grapes

// Duplicate the list of fruits in an enum
if (user.FavouriteFruit.ID == (int)Fruits.Grapes)

ou algo mais?

Como é óbvio que o FavouriteFruit será utilizado em toda a aplicação, a lista pode ser adicionada ou editada.

Alguém pode decidir que deseja que 'Grapes' seja renomeado para 'Grape' e isso, é claro, quebrará a opção de string codificada.

O ID codificado não é totalmente claro, embora, como mostrado, você possa adicionar um comentário para identificar rapidamente qual item ele é.

A opção enum envolve a duplicação de dados do banco de dados, o que parece errado, já que pode ficar fora de sincronia.

De qualquer forma, agradecemos antecipadamente por quaisquer comentários ou sugestões.

    
por Kate 03.12.2015 / 13:26
fonte

7 respostas

30

Evite strings e constantes mágicas a todo custo. Elas estão completamente fora de questão, não devem ser consideradas como opções. Isso parece deixá-lo com apenas uma opção viável: identificadores, isto é, enums. No entanto, há também mais uma opção, que na minha opinião é a melhor. Vamos chamar essa opção de "Objetos pré-carregados". Com objetos pré-carregados, você pode fazer o seguinte:

if( user.FavouriteFruit.ID == MyApplication.Grape.ID )

O que aconteceu aqui é que eu obviamente carreguei toda a linha de Grape na memória, então tenho seu ID pronto para uso em comparações. Se você estiver usando o Mapeamento Objeto-Relacional (ORM), ele ficará ainda melhor:

if( user.FavouriteFruit == MyApplication.Grape )

(É por isso que eu chamo de "Objetos pré-carregados".)

Então, o que eu faço é que durante a inicialização eu carrego todas as minhas tabelas de "enumeração" (pequenas tabelas como dias da semana, meses do ano, sexos, etc.) na classe de domínio principal do aplicativo. Eu os carrego pelo nome, porque obviamente, MyApplication.Grape deve receber a linha chamada "Grape", e afirmo que todos e cada um deles são encontrados. Caso contrário, temos um erro de tempo de execução garantido durante a inicialização, que é o menos maligno de todos os erros de tempo de execução.

    
por 03.12.2015 / 15:22
fonte
6

A checagem da string é a mais legível, mas duplica: é usada tanto como um identificador quanto como uma descrição (isso pode mudar por razões não relacionadas).

Eu costumo dividir as duas tarefas em campos separados:

id  code    description
 1  grape   Grapes
 2  apple   Apple

Onde a descrição pode mudar (mas não "Uvas" para "Banana"), mas o código não pode mudar, nunca.

Embora isso seja principalmente porque nossos IDs são quase sempre gerados automaticamente e, portanto, não são uma boa opção. Se você pode escolher ids livremente, talvez você possa garantir que eles estão sempre corretos e usá-los.

Além disso, com que frequência alguém edita "Grapes" para "Grape"? Talvez nada disso seja necessário.

    
por 03.12.2015 / 14:44
fonte
3

O que você está esperando aqui é que a lógica de programação seja automaticamente adaptável à mudança de dados. Opções estáticas simples como Enum não funcionam aqui porque você não pode adicionar enums extras no tempo de execução.

Alguns padrões que tenho visto:

  • Enums + default para proteger contra uma nova entrada no banco de dados, arruinando o dia do seu programa.
  • Codificação de ações a serem executadas (lógica biz) no próprio banco de dados. Em muitos casos, isso é muito possível porque muitas lógicas são reutilizadas. A implementação da lógica deve estar no programa.
  • Atributos / colunas extras no banco de dados para marcar o novo valor como 'a ser ignorado' no programa até que o programa seja implementado corretamente.
  • Falha nos mecanismos rápidos ao redor do caminho do código que carrega / recarrega os valores do banco de dados. (Se a ação correspondente não estiver no programa E não estiver marcada para ser ignorada, não leve a atualização).

Em geral, eu gosto de dados sendo completos em termos de referências a ações que isso implica - mesmo que as ações em si possam ser implementadas em outro lugar. Qualquer código que esteja determinando ações independentes dos dados acaba de fragmentar sua representação de dados, o que provavelmente irá divergir e levar a bugs.

    
por 03.12.2015 / 15:03
fonte
3

Armazená-los em ambos os lugares (em uma tabela e em um ENUM) não é tão ruim assim. O raciocínio é o seguinte:

Ao armazená-los em uma tabela de banco de dados, podemos impor a integridade referencial no banco de dados por meio de chaves estrangeiras. Então, quando você associa uma pessoa ou qualquer entidade a uma fruta, é apenas uma fruta que existe na tabela do banco de dados.

Armazená-los como um ENUM também faz sentido porque podemos escrever código sem seqüências de caracteres mágicas e isso torna o código mais legível. Sim, eles precisam manter em sincronia, mas realmente seria difícil adicionar uma linha ao ENUM e uma nova instrução insert ao banco de dados.

Uma coisa, quando um ENUM é definido, não altera seu valor. Por exemplo, se você tivesse:

  • Apple
  • Uva

NÃO renomeie Grape para Grapes. Basta adicionar um novo ENUM.

  • Apple
  • Uva
  • Uvas

Se você precisar migrar dados, aplique uma atualização para mover toda a uva para a uva.

    
por 03.12.2015 / 15:36
fonte
1

Você está certo em fazer essa pergunta, é uma boa pergunta, na verdade, ao tentar se defender contra a avaliação de condições imprecisas.

Dito isso, a avaliação (suas condições de if ) não precisa necessariamente ser o foco de como você contorna isso. Em vez disso, preste atenção ao modo como você propaga as alterações que causariam um problema "fora de sincronia".

Aproximação de Cadeias

Se você precisar usar strings, por que não expor a funcionalidade de alterar a lista por meio da interface do usuário? Projete o sistema para que, ao alterar Grape para Grapes , por exemplo, você atualize todos os registros que atualmente fazem referência a Grape .

Abordagem de ID

Eu sempre prefiro fazer referência a um ID, apesar do comprometimento de alguma legibilidade. The list may be added to pode ser novamente algo do qual você seria notificado se expusesse esse recurso da interface do usuário. Se você estiver preocupado com a reordenação de itens que alteram o id, propague essa alteração para todos os registros dependentes novamente. Da mesma forma acima. Outra opção (seguindo a convenção de normalização adequada, seria ter uma coluna enum / id - e referenciar uma tabela FruitDetail mais detalhada, que tem uma coluna 'Order' que você pode pesquisar).

De qualquer forma, você pode ver que estou sugerindo controlar a alteração ou atualização de sua lista. Se você fizer isso através do uso de um ORM ou de algum outro acesso a dados, isso é determinado pelos detalhes da sua tecnologia. O que você está, essencialmente, fazendo é exigir que as pessoas que estão longe do DB para tais mudanças - o que eu acho que está bem. A maioria dos principais CRMs fará o mesmo requisito.

    
por 03.12.2015 / 14:09
fonte
0

Um problema muito comum. Embora a duplicação do lado do cliente de dados possa parecer violar os princípios DRY , é realmente devido à diferença de paradigma entre o camadas.

Ter o enum (ou qualquer outro) fora de sintonia com o banco de dados também não é tão incomum. Você pode ter empurrado outro valor para uma tabela de metadados para suportar um novo recurso de relatórios que ainda não é usado no código do lado do cliente.

Às vezes acontece o contrário também. Um novo valor enum é adicionado ao lado do cliente, mas a atualização do banco de dados não pode ocorrer até que o DBA possa aplicar as alterações.

    
por 03.12.2015 / 13:51
fonte
0

Supondo que estamos falando sobre o que é essencialmente uma pesquisa estática, a terceira opção - o enum - é basicamente a única opção sensata. É o que você faria se o banco de dados não estivesse envolvido, então faz sentido.

A questão então se torna a de como manter as enums e as tabelas static / lookup no banco de dados em sincronia e, infelizmente, isso não é um problema para o qual ainda tenho uma resposta completa.

Por opção, faço toda a manutenção do meu esquema no código e, portanto, posso manter uma relação entre uma compilação do aplicativo e uma versão do esquema esperada, portanto é fácil manter a pesquisa e o enum em sincronia, mas é algo que você tem lembrar de fazer. Seria melhor se fosse mais automatizado (e também um teste de integração automatizado para garantir que as enums e as pesquisas correspondessem ajudaria), mas isso não é algo que eu já implementei.

    
por 03.12.2015 / 14:16
fonte

Tags