Pour un ORM prenant en charge la validation des données, les contraintes doivent-elles également être appliquées dans la base de données?


13

J'ai toujours appliqué des contraintes au niveau de la base de données en plus de mes modèles (ActiveRecord). Mais je me demandais si c'était vraiment nécessaire?

Un peu de fond

J'ai récemment dû tester à l'unité une méthode de génération automatique d'horodatage de base pour un modèle. Normalement, le test crée une instance du modèle et l'enregistre sans validation. Mais il existe d'autres champs obligatoires qui ne peuvent pas être annulés dans la définition de la table, ce qui signifie que je ne peux pas enregistrer l'instance même si j'ignore la validation ActiveRecord. Je pense donc que je devrais supprimer ces contraintes de la base de données elle-même et laisser l'ORM les gérer?

Avantages possibles si je saute les contraintes en db, imo -

  • Peut modifier une règle de validation dans le modèle, sans avoir à migrer la base de données.
  • Peut ignorer la validation lors des tests.

Désavantage possible?

S'il est possible que la validation ORM échoue ou soit contournée, cependant, la base de données ne vérifie pas les contraintes.

Qu'est-ce que tu penses?

EDIT Dans ce cas, j'utilise le framework Yii , qui génère le modèle à partir de la base de données, donc les règles de base de données sont également générées (bien que je puisse toujours les écrire moi-même après la génération).


3
Si les données de votre base de données peuvent être modifiées régulièrement sans utiliser votre ORM (autres applications sans votre ORM ou, pire, accès direct aux bases de données par les utilisateurs), la validation doit vraiment être dans la base de données.
Marjan Venema

Réponses:


16

Votre principe directeur devrait être de ne pas vous répéter :

En génie logiciel, Don't Repeat Yourself (DRY) est un principe de développement logiciel visant à réduire la répétition d'informations de toutes sortes, particulièrement utile dans les architectures multi-niveaux. Le principe DRY est énoncé comme «chaque élément de connaissance doit avoir une représentation unique, sans ambiguïté et faisant autorité au sein d'un système».

L'ORM est essentiellement une couche supplémentaire (ou un niveau si vous préférez), reposant confortablement entre votre application et votre (vos) stockage (s) de données. Vos contraintes doivent être en un seul endroit, et un seul endroit, que ce soit l'ORM ou le stockage de données, sinon assez tôt vous finirez par en conserver différentes versions. Vous ne voulez vraiment pas faire ça.

Cependant, dans la pratique, la plupart des ORM à moitié décents génèrent automatiquement une grande partie de vos modèles à partir de votre schéma de données. Bien qu'il y ait toujours duplication, les chances d'enfer de maintenance sont minimes puisque le code ORM dupliqué est généré suivant le même modèle à chaque fois. Il serait idéal de ne pas avoir de code en double, mais les contraintes générées automatiquement sont la prochaine meilleure chose.

De plus, avoir vos contraintes au même endroit ne signifie pas nécessairement que vous devriez avoir toutes vos contraintes au même endroit. Certaines, comme les contraintes d'intégrité référentielle, peuvent être plus adaptées au stockage de données (mais peuvent être perdues si vous passez à un autre stockage de données), et certaines, principalement celles qui concernent une logique métier complexe, conviennent mieux à votre ORM. Il serait préférable d'avoir toutes vos pommes dans le même panier, mais…

Les échecs

Vous mentionnez l'échec de l'ORM. Cela n'a absolument rien à voir avec votre question, votre application doit considérer l'ORM et le (s) stockage (s) de données comme une seule entité. S'il échoue, il échoue, contourner l'ORM pour parler directement au stockage de données n'est pas une bonne idée.

Contourner l'ORM pour autre chose

Ce n'est pas non plus une bonne idée. Cependant, cela peut se produire pour diverses raisons:

  1. Parties héritées de l'application qui ont été créées avant l'introduction de l'ORM.

    C'est difficile, et c'est exactement la situation à laquelle je fais face en ce moment , d'où ma répétition constante de «l'enfer de la maintenance». Soit vous continuez à maintenir les parties non ORM, soit vous les réécrivez pour utiliser l'ORM. La deuxième option pourrait avoir plus de sens au départ, mais c'est une décision qui est uniquement basée sur ce que font exactement ces parties de votre application et sur la valeur d'une réécriture complète à long terme.

    Essayez de changer une clé dans une table MySQL de 2 * 10 ^ 8 lignes mal conçue (sans temps d'arrêt) et vous comprendrez d'où je viens.

  2. Parties non héritées de l'application qui doivent absolument parler directement au stockage de données:

    Encore plus délicat. Les ORM sont des outils sophistiqués, et ils s'occupent de presque tout, mais parfois ils gênent ou sont même absolument inutiles. Le mot à la mode (vraiment la phrase à la mode) est une non-correspondance d'impédance relationnelle-objet , il suffit de dire qu'il n'est techniquement pas possible pour votre ORM de faire tout ce que fait votre base de données relationnelle, et pour certaines choses qu'ils font, il y a une pénalité de performance significative.

commentaires

Du point de vue de l'intégrité des données, les contraintes DOIVENT être sur la base de données et DEVRAIENT être sur l'application. Que se passe-t-il si votre application est accessible à partir d'une application Web et d'une application de bureau, d'une application mobile ou d'un service Web? - Luiz Damim

C'est là que l'ajout d'une couche supplémentaire serait extrêmement utile, et si nous parlons d'une application Web, j'irais avec une API REST. Une conception trop simpliste pour cela serait:

entrez la description de l'image ici

L'ORM se situerait entre l'API et les stockages de données, et tout ce qui se trouve derrière l'API (y compris celle-ci) serait considéré comme une entité unique issue des différentes applications.


Normalement, vous définiriez un schéma dans votre ORM qui sera ensuite mis en miroir dans la base de données afin d'avoir un deuxième niveau d'assurance.
Josh K

2
@JoshK Vous dites deuxième niveau d'assurance, je dis l'enfer de la maintenance. Mais je ne dis pas que vous n'avez pas raison ...
yannis

Logique. Je suis sur cette voie maintenant. Merci!
na

1
Une fois que vous avez dépassé le point où un ou deux développeurs font du code et des bases de données, cela devient un mal nécessaire. Si vous utilisez un bon ORM, il générera également des migrations pour vous. Lorsque vous atteignez le point d'avoir un DBA dédié, il n'y a pas de moyen de le contourner, ils ne laisseront pas les tables flotter sans contraintes. Un moyen simple d'empêcher les gens de s'inscrire sans e-mail crée une contrainte de niveau de stockage.
Josh K

1
Du point de vue de l'intégrité des données, les contraintes DOIVENT être sur la base de données et DEVRAIENT être sur l'application. Que se passe-t-il si votre application est accessible à partir d'une application Web et d'une application de bureau, d'une application mobile ou d'un service Web?
Luiz Damim

20

C'est en fait une question très difficile à répondre et j'ai trouvé que c'était un sujet très controversé.

Comme Yannis Rizos l'a souligné dans sa réponse, avoir la logique de contrainte à la fois dans la base de données et dans la couche ORM semblerait violer DRY, ce qui "peut conduire à des cauchemars de maintenance, une mauvaise factorisation et des contradictions logiques".

Toutefois, la suppression de la logique de contrainte de la base de données et sa conservation uniquement au niveau de la couche ORM ne fonctionnerait pas si vous rencontrez l'une des conditions suivantes:

  1. Mises à jour manuelles de la base de données (elles semblent se produire dans toutes les entreprises)

  2. Mises à jour de base de données à partir d'un autre système qui ne peut pas toujours partager facilement la logique de contrainte ORM (ex / un script Perl qui effectue des tâches de routine lorsque la couche ORM est implémentée dans Hibernate et utilisée par une application Java pour l'activité quotidienne)

Cela suggérerait que vous n'ajoutiez que la logique de contrainte à la base de données et que vous la supprimiez de votre couche ORM afin d'éviter une violation DRY. Cependant, cela peut entraîner des cas où le code d'application ne parvient pas à capturer le problème réel et à le transmettre à l'utilisateur (bien qu'en tant que développeur déboguant le problème, vous le puissiez très probablement). Cela peut ne pas être acceptable pour certains projets.

Votre dernière option est d' automatiser la création des contraintes dans l'ORM (et tout autre système) à partir des contraintes DB (ou, vraiment ... vice versa). Bien que vous finirez par vous retrouver avec deux ou plusieurs implémentations des contraintes, ce ne sera pas une violation du principe DRY comme décrit dans "The Pragmatic Programmer" car ils recommandent l'utilisation de la génération de code pour éviter les violations DRY. Bien sûr, ce n'est pas aussi simple que cela, car, par exemple, chaque modification d'une contrainte de base de données peut forcer une reconstruction et un redéploiement de toutes vos applications qui l'utilisent (ce qui n'est pas trivial pour automatiser).

En réalité, cela devrait être évalué au cas par cas . Je peux vous dire qu'à ce stade, j'ai été confronté à des regards vides lorsque je suggère de ne pas répéter la logique de contrainte.


2
Je viens de quitter le travail et je pensais étendre ma réponse pour qu'elle soit plus ou moins ce que vous venez de publier. Bonne réponse!
yannis

3

Je voudrais certainement ajouter des contraintes à la base de données comme option par défaut. En effet, pour une entreprise, les données sont primordiales et la qualité des données est primordiale. @Yannis Rizos a introduit le principe DRY dans la discussion. Un autre principe est la défense en profondeur. Pour les données, j'utiliserais ce principe.

J'ai travaillé dans de vraies entreprises où la base de données a créé des données il y a 30 ans. Il était et est toujours accessible par l'application COBOL et maintenant par une application .Net. Dans 10 ans, ce sera peut-être une application de vendeur, qui sait. Il y a eu une fusion et des millions de lignes de données ont été converties et migrées de l'autre société vers cette base de données à l'aide de SQL. Aucun ORM ne peut le faire. En fin de compte, les données restent, les applications changent, la façon dont les données sont générées change. Alors pourquoi ne pas réduire les risques de corruption de données?


2

Je pense que vous faites les deux dans une certaine mesure.

  • Les principales contraintes devraient vivre dans l'ORM - les langages de programmation sont beaucoup plus flexibles, il est plus facile à tester et plus facile à modifier lorsque les exigences changent; pas besoin de vous soucier des correctifs DDL au moins. Et vous évitez généralement les problèmes de régression des données difficiles à tester.

  • Certaines contraintes très dures et rapides devraient également vivre dans la base de données. Je ne parle pas de noms non nullables, par exemple. Je parle de choses comme l'intégrité référentielle ou nécessitant des identifiants absolument cruciaux. Exigences structurelles pour que votre code n'ait pas besoin de traiter "et si la Commande a un produit inexistant".


1

La base de données est IMO le seul endroit où DRY peut être violé, car si quelque chose contourne votre ORM et contient de mauvaises données, c'est tout. Jeu terminé. Avoir des données corrompues est le coup fatal.


Base de données uniquement? Je peux penser à de nombreux cas où le comportement associé aux données devrait exister sur plusieurs couches (logique ou physique) même si les données ne sont pas persistées du tout. Parfois, il est possible d'avoir un seul code source et de réduire la "duplication" aux DLL installées.
mike30
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.