Les ORM permettent-ils la création de modèles de domaine riches?

21

Après avoir utilisé Hibernate sur la plupart de mes projets pendant environ 8 ans, j'ai atterri sur une entreprise qui décourage son utilisation et souhaite que les applications interagissent uniquement avec la base de données via des procédures stockées.

Après avoir fait cela pendant quelques semaines, je n'ai pas pu créer un modèle de domaine riche de l'application que je commence à construire, et l'application ressemble simplement à un script transactionnel (horrible).

Certains des problèmes que j'ai trouvés sont:

  • Impossible de naviguer dans le graphique d'objet car les procédures stockées chargent simplement la quantité minimale de données, ce qui signifie que parfois nous avons des objets similaires avec des champs différents. Un exemple est: nous avons une procédure stockée pour récupérer toutes les données d'un client, et une autre pour récupérer les informations de compte ainsi que quelques champs du client.
  • Une grande partie de la logique se retrouve dans des classes auxiliaires, de sorte que le code devient plus structuré (avec des entités utilisées comme anciennes structures C).
  • Un code d'échafaudage plus ennuyeux, car il n'y a pas de cadre qui extrait les jeux de résultats d'une procédure stockée et les place dans une entité.

Mes questions sont:

  • quelqu'un a-t-il été dans une situation similaire et n'était pas d'accord avec l'approche de la procédure de magasin? Qu'est-ce que tu as fait?
  • Y a-t-il un réel avantage à utiliser des procédures stockées? à part le point idiot de "personne ne peut publier une table de baisse".
  • Existe-t-il un moyen de créer un domaine riche à l'aide de procédures stockées? Je sais qu'il est possible d'utiliser AOP pour injecter des DAO / référentiels dans des entités afin de pouvoir naviguer dans le graphique d'objet. Je n'aime pas cette option car elle est très proche du vaudou.

Conclusion

Tout d'abord, merci à tous pour vos réponses. La conclusion que je suis arrivée est que les ORM ne permettent pas la création de modèles de domaine riche (comme certaines personnes l'ont mentionné), mais cela simplifie la quantité de travail (souvent répétitif). Ce qui suit est une explication plus détaillée de la conclusion, mais n'est basé sur aucune donnée solide.

La plupart des applications demandent et envoient des informations à d'autres systèmes. Pour ce faire, nous créons une abstraction dans les termes du modèle (par exemple, un événement commercial) et le modèle de domaine envoie ou reçoit l'événement. L'événement a généralement besoin d'un petit sous-ensemble d'informations du modèle, mais pas de l'ensemble du modèle. Par exemple, dans une boutique en ligne, une passerelle de paiement demande des informations sur l'utilisateur et le total pour facturer un utilisateur, mais ne nécessite pas l'historique des achats, les produits disponibles et toute la base de clients. L'événement a donc un petit ensemble de données spécifiques.

Si nous prenons la base de données d'une application comme un système externe, nous devons créer une abstraction qui nous permet de mapper les entités du modèle de domaine à la base de données ( comme l'a mentionné NimChimpsky , en utilisant un mappeur de données). La différence évidente est que nous devons maintenant créer à la main un mappage pour chaque entité de modèle avec la base de données (un schéma hérité ou des procédures stockées), avec la douleur supplémentaire que, puisque les deux ne sont pas synchronisés, une entité de domaine peut mapper partiellement à une entité de base de données (par exemple, une classe UserCredentials qui ne contient que le nom d'utilisateur et le mot de passe est mappée à une table Users qui a d'autres colonnes), ou une entité de modèle de domaine peut mapper vers plusieurs entités de base de données (par exemple, s'il y a un à un un mappage sur la table, mais nous voulons toutes les données dans une seule classe).

Dans une application avec quelques entités, la quantité de travail supplémentaire peut être petite s'il n'y a pas besoin de traverser les entités, mais elle augmente quand il y a un besoin conditionnel de traverser les entités (et donc nous pourrions vouloir implémenter une sorte de `` paresseux '' chargement'). Au fur et à mesure qu'une application grandit pour avoir plus d'entités, ce travail augmente simplement (et j'ai l'impression qu'il augmente de façon non linéaire). Mon hypothèse ici, est que nous n'essayons pas de réinventer un ORM.

L'un des avantages du traitement de la base de données comme un système externe est que nous pouvons coder les situations dans lesquelles nous voulons que 2 versions différentes d'une application s'exécutent, dans lesquelles chaque application a un mappage différent. Cela devient plus intéressant dans le scénario de livraisons continues à la production ... mais je pense que c'est aussi possible avec des ORM dans une moindre mesure.

Je vais rejeter l'aspect sécurité, au motif qu'un développeur, même s'il n'a pas accès à la base de données, peut obtenir la plupart sinon la totalité des informations stockées dans un système, simplement en injectant du code malveillant (par exemple. Je ne peux pas croire que j'ai oublié de supprimer la ligne qui enregistre les détails de la carte de crédit des clients, cher seigneur! ).


Petite mise à jour (6/6/2012)

Les procédures stockées (au moins dans Oracle) empêchent de faire quelque chose comme la livraison continue avec zéro temps d'arrêt, car toute modification de la structure des tables invalidera les procédures et les déclencheurs. Ainsi, pendant la mise à jour de la base de données, l'application sera également arrêtée. Oracle fournit une solution pour cette redéfinition basée sur l'édition , mais les quelques administrateurs de base de données que j'ai interrogés sur cette fonctionnalité ont mentionné qu'elle était mal implémentée et qu'ils ne la mettraient pas dans une base de données de production.

Augusto
la source
Eh bien, évidemment, vous pouvez faire ce que fait Hibernate et utiliser l'héritage pour générer un objet proxy dynamique, qui vous permet de récupérer le graphe de l'objet. C'est extrêmement hacky avec SP cependant: D
Max
Je finirais donc par réinventer la moitié de l'hibernation, sans les 10+ années d'expérience de l'équipe d'hibernation :).
Augusto
1
Tout DBA doit empêcher la suppression de tables particulières par certains utilisateurs. Peu importe comment vous essayez de le faire.
JeffO
1
Vous pourriez jeter un œil à Mybatis - il pourrait fournir la fonctionnalité dont vous avez besoin. C'est moins un ORM qu'un cadre de cartographie. Vous pouvez écrire du SQL comme bon vous semble et dire à Mybatis où le mettre sur votre modèle objet. Il gérera les graphiques d'objets volumineux avec plusieurs requêtes, ce qui ressemble à la situation que vous avez (beaucoup de procédures stockées fines).
Michael K
1
@Augusto: J'ai été dans une situation similaire, non pas en raison de l'utilisation de SP, mais en raison de l'utilisation d'un cadre de mappage propriétaire qui ne prend pas en charge les relations d'objet. Nous avons passé des jours à écrire du code qui pouvait être écrit en quelques minutes en utilisant un ORM approprié. Je n'ai jamais résolu ce problème.
kevin cline

Réponses:

16

Votre application doit toujours être modélisée à partir des principes de conception pilotés par domaine. Que vous utilisiez un ORM, un JDBC droit, appeler des SP (ou autre) ne devrait pas avoir d'importance . Espérons qu'une couche mince faisant abstraction de votre modèle à partir des SP devrait faire l'affaire dans ce cas. Comme l'a indiqué une autre affiche , vous devez afficher les SP et leurs résultats en tant que service et mapper les résultats sur votre modèle de domaine.

Martijn Verburg
la source
Martijn, je suis d'accord que l'application doit être modélisée en utilisant les principes DDD, mais le problème auquel je suis confronté (et dites-moi s'il y a une solution !!) est que certains procs stockés retournent très peu d'informations à instantiante une entité DDD. Veuillez voir ce commentaire où j'ai expliqué un peu plus sur les informations que renvoient les procédures stockées. Je pourrais contourner cela, en invoquant plus d'un proc stocké, et par exemple en récupérant tous les détails de l'utilisateur, puis en invoquant un autre pour récupérer toutes les informations de compte, mais cela ne va pas :).
Augusto
1
@Augusto Eh bien ... vous êtes développeur d'applications, vous devez donc décider s'il est logique que certains objets existent avec certains champs définis sur NULL. Si cela a du sens (pour certaines tâches par exemple) alors laissez-le faire. Sinon, demandez à l'auteur de SP de fournir plus de données, afin de pouvoir créer vos objets.
Jacek Prucia
Et pour ajouter au commentaire de Jacek - il est en fait parfaitement acceptable d'appeler plus de 2 procs stockés, encore une fois, considérez-les comme deux services distants que vous devez appeler afin de créer votre modèle de domaine, rien de mal à cela :-).
Martijn Verburg
@Martijn: D'après mon expérience, une couche mince n'est pas suffisante. Le code de mappage peut être considérablement plus long que la logique métier sous-jacente.
kevin cline
@Kevin cline - Bon point, ont mis 'espérons-le' dans la réponse :-)
Martijn Verburg
5

Y a-t-il un réel avantage à utiliser des procédures stockées?

Dans le monde financier (et dans les endroits où la conformité Sarbanes-Oxley est requise), vous devez être en mesure d'auditer les systèmes pour vous assurer qu'ils font ce qu'ils sont censés faire. Dans ces cas, il est beaucoup plus facile de garantir la conformité lorsque tous les accès aux données se font via des procédures stockées. Et lorsque tout SQL ad-hoc est supprimé, il est beaucoup plus difficile de masquer les choses. Pour un exemple de la raison pour laquelle ce serait une «bonne chose», je vous renvoie au document classique de Ken Thompson, Reflections on Trusting Trust .

Tangurena
la source
oui un million de fois oui! Vous devez également vous assurer que les utilisateurs ne peuvent pas faire tout ce qu'ils ne sont pas censés faire, notamment le fait de ne pas avoir de droits directs sur les tables et les proc stockés.
HLGEM
1
Je travaille pour une entreprise publique et nous sommes conformes SOX. C'est peut-être ma mauvaise connaissance de l'audit, mais je ne vois pas la différence entre faire l'audit au niveau de la base de données (via des proc stockés) ou au niveau de l'application. Chaque application doit avoir son propre schéma de base de données et ce schéma n'est accessible qu'à partir de l'application, plutôt que partagé entre différentes applications.
Augusto
lien cassé ...
Alex R
@AlexR, liaison fixe
Tangurena
2

Les procédures stockées sont beaucoup plus efficaces que le code SQL côté client. Ils précompilent SQL dans la base de données, ce qui lui permet également d'effectuer certaines optimisations.

Sur le plan architectural, un SP renvoie les données minimales requises pour une tâche, ce qui est bon car cela signifie que moins de données sont transférées. Si vous avez une telle architecture, vous devez considérer la base de données comme un service (pensez-y comme un service Web et chaque SP est une méthode à appeler). Travailler avec cela ne devrait pas être un problème, alors qu'un ORM vous guide dans l'utilisation de données distantes comme si elles étaient locales, vous incitant ainsi à introduire des problèmes de performances si vous n'y faites pas attention.

J'ai été dans des situations où nous avons utilisé complètement les SP, la base de données a fourni une API de données et nous l'avons utilisée. Cette application particulière était à très grande échelle et fonctionnait incroyablement bien. Je n'aurai plus rien de mal à dire sur les SP après ça!

Il y a un autre avantage: les administrateurs de bases de données écriront toutes vos requêtes SQL pour vous et gèreront avec plaisir toute la hiérarchie relationnelle dans la base de données, vous n'avez donc pas à le faire.

gbjbaanb
la source
3
gbjbaanb, la plupart de ce que vous avez dit est vrai pour les anciennes bases de données. La plupart des bases de données plus récentes recompilent les requêtes assez souvent pour décider quelles nouvelles optimisations utiliser (même si elles sont stockées produites). Je suis d'accord avec ce que vous avez dit sur l'utilisation de la base de données en tant que système externe, mais je vois aussi que cela représente beaucoup de travail, car l'application possède la base de données et les deux devraient être synchronisées autant que possible. Par exemple avec la dénomination de table / classes et champs / colonnes. En outre, l'approche consistant à laisser les administrateurs de bases de données rédiger les procédures sent comme des silos de développement, plutôt que d'avoir une équipe multidisciplinaire.
Augusto
7
Les SP ne sont pas nécessairement toujours plus efficaces et je pense que transmettre SQL aux DBA est une mauvaise façon de procéder. En tant qu'expert du domaine, le développeur doit savoir quelles données il souhaite obtenir et comment les obtenir
Martijn Verburg
1
C'est une bonne réponse, mais d'après mon expérience, la plupart des clients n'ont pas réellement besoin des gains de performances pour contrôler l'accès aux données via des procédures stockées par rapport à l'inconvénient d'utiliser pleinement vos outils ORM sur la couche application. Plus souvent qu'autrement, je vois ces décisions architecturales prises dans les magasins de logiciels où ils ont besoin de justifier les salaires gonflés des programmeurs de procédures stockées "barbe grise" qui n'ont pas d'autres compétences.
maple_shaft
1
@Augusto the approach of letting the DBAs write the procedures smells like development silos+100 internets à vous pour ce joyau de vérité. J'ai toujours vu cela comme le cas où l'accès aux données était contrôlé par des procédures stockées.
maple_shaft
1
@maple_shaft: pourquoi, les DBA qui écrivent des SP ne sont-ils pas considérés comme faisant partie de l'équipe de développeurs? Là où cela fonctionne, ce sont des codeurs spécialisés qui connaissent très bien cet aspect du système, bien mieux que la plupart des développeurs à usage général. Cela pourrait être le problème qui a donné lieu à la popularité des ORM. Je veux dire, personne ne penserait à deux fois avant qu'un designer fasse l'interface graphique, alors pourquoi la haine pour un architecte de données qui fait le schéma?
gbjbaanb
2

Ce qui arrive souvent, c'est que les développeurs utilisent incorrectement leurs objets ORM comme modèles de domaine.

Ceci est incorrect et lie votre domaine directement à votre schéma de base de données.

Ce qui devrait vraiment être, c'est des modèles de domaine séparés aussi riches que vous le souhaitez et utilisez la couche ORM séparément.

Cela signifie que vous devrez mapper entre chaque ensemble d'objets.

ozz
la source
1
C'est une bonne idée, mais pour les petits projets, cela commence vraiment à se sentir exagéré. Cette approche nécessite également une couche de traduction entre la couche de persistance ORM et le modèle de domaine.
maple_shaft
@maple_shaft a accepté, et c'est ce que je voulais dire par "mappage" :-)
ozz
@Ozz, la façon dont j'ai travaillé est exactement cela, les classes d'entités SONT le modèle de domaine (et je pourrais ajouter avec beaucoup de succès). Je suis d'accord qu'il lie le modèle de domaine au schéma, mais c'est exactement ce que je veux, car j'utilise la convention sur la configuration, et le bel effet secondaire est que si je vois un champ sur une entité, je n'ai pas besoin de réfléchir sur le nom de la table et de la colonne où ces informations sont stockées.
Augusto
@Augusto Je l'ai fait aussi! et comme le dit maple_shaft, c'est bien pour les petites applications de style CRUD, mais il y a beaucoup de problèmes comme l'OP le découvre. Un exemple pourrait être où vous avez une table de mappage plusieurs à plusieurs, par exemple: StudentClasses, qui mappe les étudiants à leurs classes et contient simplement StudentID et classID, vous ne voudriez pas nécessairement mapper cela dans votre domaine. C'est juste un exemple rapide du haut de ma tête.
2012
2
@Ozz: Votre commentaire semble contredire l'idée même d'un ORM. Un ORM ne "lie pas votre domaine directement à votre schéma de base de données". L'ORM mappe votre domaine à un schéma de base de données, sans avoir besoin d'une couche DAO distincte. C'est tout l'intérêt d'un ORM. Et la plupart des ORM gèrent très bien les mappages plusieurs à plusieurs, sans modèle de domaine requis pour la table de mappage.
kevin cline
1

Vos objets de domaine peuvent être remplis à votre guise, il n'est pas nécessaire d'utiliser Hibernate. Je pense que le terme approprié est mappeur de données . Il est très possible que vos données persistantes aient une structure complètement différente de vos objets de domaine.

NimChimpsky
la source
Nous utilisons actuellement des mappeurs de données, mais le problème est qu'ils procs stockés renvoient un ensemble minimal de données, ce qui parfois n'est pas suffisant pour remplir un objet (nous devrions peut-être autoriser les procédures stockées à renvoyer plus d'informations). Par exemple, un processus de magasin peut renvoyer un e-mail d'utilisateur, un prénom, un nom de famille; tandis qu'un autre ajoute un ID utilisateur et une adresse. Étant donné que les données sont différentes, nous utilisons différents objets pour stocker les données, ce qui signifie que nous avons différentes classes «utilisateur». J'essaie d'éviter d'utiliser l'héritage ici, car je pense que c'est une mauvaise utilisation.
Augusto
@Augusto: Interfaces?
Kramii Reinstate Monica
@Karmii, je ne pense pas que les interfaces aident ici, car nous aurions alors besoin de dupliquer la logique dans différentes classes. Ou nous pourrions utiliser des interfaces et ensuite déléguer le traitement à une classe d'assistance, mais ce n'est pas vraiment OO :(.
Augusto
1
@Augusto Je ne comprends pas le problème: "les procs stockés renvoient un ensemble minimal de données, ce qui parfois n'est pas suffisant pour remplir un objet" Donc vous modifiez le sproc ou en créez un autre, puis laissez le mappeur de données faire le mappage
NimChimpsky