Onde validar regras de modelo de domínio que dependem do conteúdo do banco de dados?

10

Estou trabalhando em um sistema que permite aos administradores definir formulários que contêm campos. Os Formulários definidos são então utilizados para inserir dados no sistema. Às vezes, os formulários são preenchidos por um ser humano por meio de uma GUI, às vezes, o formulário é preenchido com base nos valores relatados por outro sistema.

Para cada campo, o administrador pode definir uma regra de validação que limita os valores permitidos para o campo. As regras de validação podem ser de "o valor inserido no campo deve ser verdadeiro ou falso" a "o valor inserido no campo deve existir na coluna A da tabela B no banco de dados". O Administrador pode a qualquer momento alterar a Regra de Validação para o Campo.

Nesse cenário, qual, na sua opinião, é o local mais adequado para validar se cada campo foi preenchido corretamente? Atualmente, tenho duas abordagens principais em mente:

Opção 1: validar no modelo de domínio

Cada objeto Field conteria a regra de validação especificada pelo administrador. Os objetos Field também teriam uma referência a um IValidator. Quando é feita uma tentativa de definir o valor do campo, o campo deve passar o valor fornecido e a regra de validação para o validador IV. Se o valor fornecido não for válido, uma ValidationException será lançada e tratada adequadamente na GUI / interface para o outro sistema.

Prós:

  • Forte proteção contra campos que recebem valores atribuídos acidentalmente que violam a regra de validação

Contras:

  • A camada de acesso a dados precisa ser capaz de ignorar a validação e criar campos que violam a regra de validação atual. Apesar de o administrador alterar a regra de validação de um campo, ainda precisamos construir objetos de campo com base nos dados antigos, por exemplo, ao renderizar um formulário preenchido anos atrás. Isso pode ser resolvido armazenando a regra de validação atual sempre que armazenar o campo.

  • Nesse design, o modelo de campo tem um link indireto para o Data Access Layer / Repository por meio do IValidator. A injeção de Serviços / Repositórios em Modelos de Domínio parece ser geralmente desaprovada .

Opção 2: validar em um serviço

Tente garantir que todas as tentativas de definir o valor de um campo passem por um serviço que garanta a manutenção da regra de validação. Se a regra de validação for violada, lance uma ValidationException.

Obviamente, a Camada de Acesso a Dados não usaria o Serviço ao criar objetos de Campo que haviam sido persistidos anteriormente no banco de dados.

Prós:

  • Não viola o pensamento "não injete Serviços / Repositórios nos seus Modelos de Domínio".

  • Não há necessidade de persistir a regra de validação atual ao persistir no campo. O Serviço pode simplesmente procurar a Regra de Validação atual para o Campo; ao analisar os dados do histórico, o valor do campo não será alterado.

Contras:

  • Não há garantia de que toda a lógica que deve usar o Serviço para definir o valor do Campo realmente o faça. Eu vejo isso como uma grande desvantagem; tudo o que parece ser necessário é alguém que esteja escrevendo "thisField.setValue (thatField.getValue ())" e a Regra de validação deste campo pode ser violada sem que ninguém seja mais sábio. Isso pode ser mitigado, garantindo que o valor do Campo corresponda à Regra de Validação quando a Camada de Acesso a Dados estiver prestes a persistir.

Atualmente, prefiro a Opção 1 ao invés da Opção 2, principalmente porque vejo isso como lógica de negócios e sinto que a Opção 2 representa um risco maior de introduzir dados incorretos no sistema. Qual opção você prefere ou existe outro design que se encaixa nesse cenário melhor do que as duas opções descritas?

Editar (complexidade das validações)

Os casos de validação que surgiram no momento são relativamente simples; o valor do campo deve ser, por exemplo, numérico, uma data, uma data com uma hora ou um valor existente em uma coluna do banco de dados. No entanto, suspeito que a complexidade aumente gradualmente ao longo do tempo. Por exemplo, a solução de validação precisa ser construída com a internacionalização em mente - coisas como Datas podem ser inseridas em uma sintaxe específica do local.

Decidi continuar com a opção 1 por enquanto, tentando tomar cuidado para não atribuir muitas responsabilidades ao modelo de domínio. Aqueles que enfrentam uma situação semelhante também podem querer consultar as questões relacionadas Validação e autorização na arquitetura em camadas e Validação de entrada de dados - Onde? Quanto? .

Lauri Harpf
fonte
Criar o modelo funciona muito bem com todas as validações. Mas quando você deseja editar ou visualizar o formulário, a validação também entra em ação. Isso gera exceção para campos vazios ou com valores inválidos - talvez alterados no banco de dados ou carregados no Excel. A solução esperada é permitir que o formulário seja exibido na atualização para que o administrador possa corrigir esses campos.
tunmise fasipe

Respostas:

4

Quão complexas são as validações? Geralmente, as validações exigem uma combinação de campos e / ou regras de negócios que dependem de campos para serem avaliadas com precisão.

Quanto mais complexas as validações, mais difícil e menos eficiente é a opção 2.

Obviamente, a camada de dados pode chamar o serviço de validação em tempo de persistência. Isso poderia ajudar a situação ímpar em que os dados estão em um estado inválido devido a uma alteração nas regras.

O outro item que vale a pena comentar é a capacidade de alterar as regras de validação sem um ciclo qa de algum tipo. Mas esse é um tópico para um segmento diferente.

Dadas as informações acima, a opção 1 parece ser a mais flexível, desde que você mantenha a disciplina, separando validação e persistência.

deschaefer
fonte
0

Talvez esteja faltando uma camada. Sem conhecer os detalhes do seu aplicativo (requisitos, arquitetura etc.), eu faria algo como o Cliente (quem quer que seja) -> Serviço de Aplicativo -> Modelo de Domínio

A camada de serviço de aplicativo pode interagir com o repositório e o modelo de domínio que contém a lógica de negócios. Então você pode ter algo como:

FieldUpdateService >> updateField(fieldId, newValue)
  List<FieldRule> rules = this.rulesRepository.findRulesFor(fieldId)
  Field field = this.fieldRepository.find(fieldId)
  try 
    field.updateValue(rules,newValue)
    fieldRepository.save(field)
  catch 
    // manage error

Se você tem um relacionamento entre um Field e suas FieldRules e usa um ORM como o Hibernate em Java, você fará apenas:

FieldUpdateService >> updateField(fieldId, newValue)
  Field field = this.fieldRepository.find(fieldId)
  try 
    field.updateValue(newValue)
    fieldRepository.save(field)
  catch 
    // manage error

Field >> updateValue(newValue)
  for rule in rules
     if !rule.isValid(newValue)
        throw ValueNotAllowedException
  this.value = newvalue

Porque o ORM consulta e instancia o gráfico de objetos que você solicita.

Em relação à sua dúvida sobre o fato de alguém poder fazer

thisField.setValue (thatField.getValue ()) "e a Regra de validação deste thisField podem ser violadas sem que ninguém seja mais sábio

Alguém também pode escrever: FieldRule alwaysReturnTrueRule = new FieldRule {isValid (newValue) {return true; }} field.updateValue ("uncheckedValue", alwaysReturnTrueRule) É um exemplo obscuro, mas o que estou dizendo é que nada o protege de um mau uso do código. Talvez boa documentação e comunicação face a face. Eu acho que nada o protege de um mau uso do código.

gabrielgiussi
fonte