Em qual camada a validação deve estar localizada?

18

Estou criando uma API Rest usando o Spring Boot e estou usando a Hibernate Validation para validar entradas de solicitação.

Mas também preciso de outros tipos de validação, por exemplo, quando os dados de atualização precisam ser verificados, se o ID da empresa não existir, quero lançar uma exceção personalizada.

Essa validação deve estar localizada na camada de serviço ou na camada do controlador?

Camada de serviço:

 public Company update(Company entity) {
    if (entity.getId() == null || repository.findOne(entity.getId()) == null) {
        throw new ResourceNotFoundException("can not update un existence data with id : " 
            + entity.getId());
    }
    return repository.saveAndFlush(entity);
}

Camada do controlador:

public HttpEntity<CompanyResource> update(@Valid @RequestBody Company companyRequest) {
    Company company = companyService.getById(companyRequest.getId());
    Precondition.checkDataFound(company, 
        "Can't not find data with id : " + companyRequest.getId());

    // TODO : extract ignore properties to constant

    BeanUtils.copyProperties(companyRequest, company, "createdBy", "createdDate",
            "updatedBy", "updatedDate", "version", "markForDelete");
    Company updatedCompany = companyService.update(company);
    CompanyResource companyResource = companyAssembler.toResource(updatedCompany);
    return new ResponseEntity<CompanyResource>(companyResource, HttpStatus.OK);
}
fdarmanto
fonte

Respostas:

8

A camada do controlador e a camada de serviço expõem determinadas interfaces. Interfaces definem contratos sobre como a interface deve ser usada. Contrato geralmente significa quais argumentos (e seus tipos e valores) são esperados, quais exceções podem ser lançadas, quais efeitos colaterais são criados etc.

Agora, sua validação é essencialmente a imposição do contrato entre o método update () do controlador e o método update () da camada de serviço. Ambos têm contrato muito semelhante; portanto, seria natural que a validação (execução do contrato) também fosse comum.

Uma maneira possível de fazer isso é separar a validação deste contrato e solicitá-lo nas duas camadas. Isso geralmente é mais claro - cada classe / método impõe seu próprio contrato, mas geralmente é impraticável por causa do desempenho (acesso ao banco de dados) ou por outros motivos.

Outra possibilidade é delegar essa validação à camada de serviço enquanto define explicitamente o comportamento em caso de falha na validação no contrato da camada de serviço. A camada de serviço normalmente retorna algum erro de validação genérico (ou lança uma exceção) e a camada do controlador deseja reagir de alguma maneira específica ao erro - nesse caso, retornaremos 400 Solicitações incorretas para sinalizar que a solicitação recebida era inválida.

Nesse design, existe o risco de muito acoplamento entre a lógica de negócios na camada de serviço (que deve ser bastante genérica) e o controlador (que lida com a lógica de integração).

Enfim, essa é uma pergunta bastante controversa e 100 pessoas responderão com 100 respostas. Esta é apenas a minha opinião.

qbd
fonte
1

A entrada deve ser verificada na camada de serviço.

E "Não é possível encontrar o ID" é condição de erro lógico. Então, deve ser jogado da camada do controlador.

Isso depende novamente de suas camadas / design.
O que uma camada de serviço deve fazer e o que é esperado da camada do controlador.

Sem papel
fonte
Uma resposta não deve estar buscando esclarecimentos adicionais da pergunta. Se a pergunta precisar de esclarecimentos, ela deve ser comentada e, possivelmente, sinalizada para fechamento, se for muito clara. Sim, eu sei que você não tem reputação por nenhuma dessas ações.
A "verificação de entrada" é ambígua. Por exemplo, eu posso colocar um atributo Obrigatório em um campo para indicar que ele deve ser preenchido, mas também posso colocar um atributo personalizado complexo que verifique, por exemplo, se um valor de campo é maior que outro. IMHO, a validação Compare "cheira" muito mais a camada de serviço de negócios do que a camada de controlador.
JustAMartin 24/07
1

As validações de hibernação são verificações da integridade dos dados. Para evitar RuntimeExceptions do bbdd. Elas são praticamente as mesmas validações que você deve controlar com o Constrains . Como apenas a camada de negócios deve alimentar a camada de persistência, você pode (ou não, até você) confiar na correção dos dados provenientes da camada de negócios

Eu não coloco validações em DAOs. Espero dados válidos das camadas superiores. Em caso de erro, delego ao bbdd a responsabilidade de estar ciente de seu conteúdo.

Em seguida, vêm as validações na camada de negócios. Todas as validações de negócios se concentraram em manter a coerência dos dados, não sua integridade .

Finalmente, faço validações anteriores na camada de controle. Aqueles relacionados apenas com essa camada.

Você verá em breve quais validações devem ser implementadas na camada de negócios. O mais comum: controle de identificação. Este pode ser facilmente implementado em ambas as camadas. Se você espera ter muitos controladores ou clientes consumindo sua camada de negócios, em vez de repetir a mesma validação em todos os lugares, será um excelente candidato a ser colocado na camada de negócios.

Às vezes, os controladores têm suas próprias regras e condições que não serão reproduzidas em nenhuma outra fachada. Em seguida, é um candidato a ser colocado em tal controlador.

Pense no que você está validando e se deseja aplicá-lo a todos, não importa o quê. Ou se for uma validação contextual ("Estou validando algo que só acontece em uma fachada de controle / exibição específica).

Laiv
fonte
0

Em nossa loja Java, dividimos intencionalmente a validação de widget da web em três operações separadas.

  1. Formatação básica - os números devem ser números; datas devem ser datas válidas etc. Geralmente, essa validação é gratuita - a estrutura da web faz isso para você ao vincular o conteúdo do widget ao modelo.
  2. Validação de widget único - a data deve estar no passado; um número inteiro deve estar entre 1 e 100; customerId deve existir no banco de dados etc. Isso pertence à camada do controlador na maioria dos casos, mas pode precisar de suporte do repositório de dados.
  3. Validação entre widgets - a data do checkout deve ser posterior à data do check-in; a data da morte não pode ser anterior à data de nascimento etc. Essa é definitivamente uma validação da regra de negócios. Também tendemos a colocar isso na camada do controlador, mas convém transferi-lo para um validador de negócios para que possa ser reutilizado.

Se a camada 1 falhar, não marcaremos 2 ou 3. Da mesma forma, se 1 for bem-sucedido e 2 falhar, não ocorreremos 3. Isso interrompe a geração de mensagens de erro espúrias.

Você está perguntando sobre valores em uma chamada REST em vez do conteúdo do widget, mas os mesmos princípios se aplicam.

kiwiron
fonte
-1

Uma abordagem orientada a testes esclarece isso, afinal, não há controlador e você deve escolher outra opção. Obviamente, as regras de negócios devem estar em um só lugar, e essa é outra restrição à sua decisão.

Hans Poo
fonte