web-dev-qa-db-fra.com

Dans quelle couche la validation doit-elle être située?

Je crée une API Rest à l'aide de Spring Boot et j'utilise Hibernate Validation pour valider les entrées de demande.

Mais j'ai également besoin d'autres types de validation, par exemple lorsque les données de mise à jour doivent être vérifiées, si l'ID de l'entreprise n'existe pas, je veux lever une exception personnalisée.

Cette validation doit-elle se situer au niveau de la couche service ou de la couche contrôleur?

Couche de service:

 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);
}

Couche de contrôleur:

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);
}
19
fdarmanto

La couche contrôleur et la couche service exposent certaines interfaces. Les interfaces définissent des contrats sur la façon dont l'interface doit être utilisée. Le contrat signifie généralement quels arguments (et ses types et valeurs) sont attendus, quelles exceptions peuvent être levées, quels effets secondaires sont créés, etc.

Maintenant, votre validation consiste essentiellement à appliquer le contrat de la méthode update () du contrôleur et la méthode update () de la couche de service. Les deux ont un contrat très similaire, il serait donc naturel que la validation (exécution du contrat) soit également courante.

Une façon possible de le faire est de séparer la validation de ce contrat et de le faire appeler dans les deux couches. Ceci est généralement plus clair - chaque classe/méthode applique son propre contrat, mais est souvent peu pratique en raison des performances (accès à la base de données) ou d'autres raisons.

Une autre possibilité consiste à déléguer cette validation à la couche service tout en définissant explicitement le comportement en cas d'échec de la validation dans le contrat de couche service. La couche de service renvoie généralement une erreur de validation générique (ou une exception de levée) et la couche de contrôleur voudra réagir d'une manière spécifique à l'erreur - dans ce cas, nous retournerons 400 Bad request pour signaler que la demande entrante n'était pas valide.

Dans cette conception, il existe un risque de trop couplage entre la logique métier dans la couche de service (qui devrait être assez générique) et le contrôleur (qui gère la logique d'intégration).

Quoi qu'il en soit, c'est une question assez controversée et 100 personnes répondront avec 100 réponses. Ceci est juste mon point de vue.

8
qbd

L'entrée doit être vérifiée dans la couche de service.

Et "Can't find id" est une condition d'erreur logique. Donc, devrait être jeté de la couche contrôleur.

Cela dépend à nouveau de votre stratification/conception.
Ce qu'une couche service est censée faire et ce qui est attendu de la couche contrôleur.

1
Paperless

Les validations en veille prolongée sont des vérifications sur intégrité des données. Afin d'éviter les RuntimeExceptions de bbdd. Ce sont à peu près les mêmes validations que vous devriez contrôler avec Contraintes. Parce que seule la couche métier devrait être alimentée la couche persistance, vous pouvez (ou non, jusqu'à vous) faire confiance à l'exactitude des données provenant de votre couche entreprise

Je ne mets pas de validations dans les DAO. J'attends des données valides des couches supérieures. En cas d'erreur je délègue au bbdd la responsabilité de connaître son contenu.

Viennent ensuite les validations au niveau de la couche métier. Toutes les validations commerciales se sont concentrées sur la conservation cohérence des données, pas leur intégrité.

Enfin je fais des validations précédentes sur la couche de contrôle. Ceux liés uniquement à une telle couche.

Vous verrez bientôt quelles validations sont censées être impliquées dans la couche métier. Le plus courant: le contrôle d'identité. Celui-ci peut facilement être implémenté sur les deux couches. Si vous vous attendez à ce que de nombreux contrôleurs ou clients consomment votre couche métier, au lieu de répéter la même validation partout, ce sera un excellent candidat à placer dans la couche métier.

Parfois, les contrôleurs ont leurs propres règles et conditions qui ne seront reproduites sur aucune autre façade. C'est alors un candidat à intégrer dans un tel contrôleur.

Pensez à ce que vous validez et si vous souhaitez l'appliquer à tout le monde, quoi qu'il arrive. Ou s'il s'agit d'une validation contextuelle ("Je valide quelque chose qui ne se produit que sur une façade de contrôle/vue particulière).

1
Laiv

Dans notre Java, nous avons intentionnellement divisé la validation du widget Web en trois opérations distinctes.

  1. Formatage de base - les nombres doivent être des nombres; les dates doivent être des dates valides, etc. Habituellement, cette validation est gratuite - le cadre Web le fera pour vous lors de la liaison du contenu du widget au modèle.
  2. Validation d'un widget unique - la date doit être antérieure à la date du jour; un entier doit être compris entre 1 et 100; customerId doit exister dans la base de données, etc. Il appartient dans la couche contrôleur dans la plupart des cas, mais peut nécessiter la prise en charge du référentiel de données.
  3. Validation croisée des widgets - la date de départ doit être postérieure à la date d'arrivée; la date de décès ne peut pas être antérieure à la date de naissance, etc. Nous avons également tendance à mettre cela dans la couche contrôleur, mais vous voudrez peut-être le déplacer dans un validateur d'entreprise afin qu'il puisse être réutilisé.

Si la couche 1 échoue, nous ne vérifions pas 2 ou 3. De même, si 1 réussit et 2 échoue, nous n'en faisons pas 3. Cela empêche la génération de messages d'erreur parasites.

Vous demandez des valeurs dans un appel REST plutôt que le contenu d'un widget, mais les mêmes principes s'appliquent.

0
kiwiron