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.