Pourquoi avons-nous besoin d'objets d'entité? [fermé]


139

J'ai vraiment besoin de voir un débat honnête et réfléchi sur les mérites du paradigme de conception d' applications d'entreprise actuellement accepté .

Je ne suis pas convaincu que les objets entité devraient exister.

Par objets d'entité, j'entends les éléments typiques que nous avons tendance à créer pour nos applications, comme "Personne", "Compte", "Commande", etc.

Ma philosophie de conception actuelle est la suivante:

  • Tout accès à la base de données doit être effectué via des procédures stockées.
  • Chaque fois que vous avez besoin de données, appelez une procédure stockée et itérez sur un SqlDataReader ou les lignes d'un DataTable

(Remarque: j'ai également construit des applications d'entreprise avec Java EE, les gens de Java veuillez remplacer l'équivalent pour mes exemples .NET)

Je ne suis pas anti-OO. J'écris beaucoup de classes à des fins différentes, mais pas des entités. J'admets qu'une grande partie des classes que j'écris sont des classes d'assistance statiques.

Je ne construis pas de jouets. Je parle d'applications transactionnelles volumineuses et volumineuses déployées sur plusieurs machines. Applications Web, services Windows, services Web, interaction b2b, vous l'appelez.

J'ai utilisé des OR Mappers. J'en ai écrit quelques-uns. J'ai utilisé la pile Java EE, CSLA et quelques autres équivalents. Je les ai non seulement utilisés, mais activement développé et maintenu ces applications dans des environnements de production.

J'en suis venu à la conclusion éprouvée au combat que les objets d'entité nous gênent et que nos vies seraient tellement plus faciles sans eux.

Prenons cet exemple simple: vous recevez un appel d'assistance concernant une certaine page de votre application qui ne fonctionne pas correctement, peut-être que l'un des champs n'est pas conservé comme il se doit. Avec mon modèle, le développeur chargé de trouver le problème ouvre exactement 3 fichiers . Un ASPX, un ASPX.CS et un fichier SQL avec la procédure stockée. Le problème, qui peut être un paramètre manquant à l'appel de procédure stockée, prend quelques minutes à résoudre. Mais avec n'importe quel modèle d'entité, vous lancerez invariablement le débogueur, commencerez à parcourir le code et vous pourrez vous retrouver avec 15 à 20 fichiers ouverts dans Visual Studio. Au moment où vous descendez au bas de la pile, vous avez oublié où vous avez commencé. Nous ne pouvons garder que tant de choses dans nos têtes à la fois. Le logiciel est incroyablement complexe sans ajouter de couches inutiles.

La complexité du développement et le dépannage ne sont qu'un côté de mon problème.

Parlons maintenant d'évolutivité.

Les développeurs se rendent-ils compte que chaque fois qu'ils écrivent ou modifient un code qui interagit avec la base de données, ils doivent faire une analyse approfondie de l'impact exact sur la base de données? Et pas seulement la copie de développement, je veux dire une imitation de la production, vous pouvez donc voir que la colonne supplémentaire dont vous avez maintenant besoin pour votre objet vient d'invalider le plan de requête actuel et un rapport qui s'exécutait en 1 seconde prendra maintenant 2 minutes, juste parce que vous avez ajouté une seule colonne à la liste de sélection? Et il s'avère que l'index dont vous avez maintenant besoin est si grand que le DBA va devoir modifier la disposition physique de vos fichiers?

Si vous laissez les gens s'éloigner trop du magasin de données physique avec une abstraction, ils créeront des ravages avec une application qui doit évoluer.

Je ne suis pas un fanatique. Je peux être convaincu si je me trompe, et peut-être que je le suis, car il y a une telle poussée vers Linq vers Sql, ADO.NET EF, Hibernate, Java EE, etc. Veuillez réfléchir à vos réponses, s'il me manque quelque chose, je veux vraiment savoir ce que c'est, et pourquoi je devrais changer ma façon de penser.

[Éditer]

Il semble que cette question est soudainement à nouveau active, alors maintenant que nous avons la nouvelle fonctionnalité de commentaire, j'ai commenté directement plusieurs réponses. Merci pour les réponses, je pense que c'est une discussion saine.

J'aurais probablement dû être plus clair que je parle d'applications d'entreprise. Je ne peux vraiment pas commenter, par exemple, un jeu qui s'exécute sur le bureau de quelqu'un ou une application mobile.

Une chose que je dois mettre ici en haut en réponse à plusieurs réponses similaires: l'orthogonalité et la séparation des préoccupations sont souvent citées comme des raisons d'aller entité / ORM. Les procédures stockées sont pour moi le meilleur exemple de séparation des préoccupations auquel je puisse penser. Si vous interdisez tout autre accès à la base de données, autrement que via des procédures stockées, vous pourriez en théorie reconcevoir l'ensemble de votre modèle de données et ne casser aucun code, à condition de conserver les entrées et les sorties des procédures stockées. Ils sont un parfait exemple de programmation par contrat (à condition d'éviter de "sélectionner *" et de documenter les jeux de résultats).

Demandez à quelqu'un qui travaille dans l'industrie depuis longtemps et qui a travaillé avec des applications à longue durée de vie: combien de couches d'applications et d'interface utilisateur se sont succédées alors qu'une base de données vivait? À quel point est-il difficile d'ajuster et de refactoriser une base de données alors qu'il existe 4 ou 5 couches de persistance différentes générant du SQL pour obtenir les données? Vous ne pouvez rien changer! Les ORM ou tout code générant du SQL verrouillent votre base de données dans la pierre .


Après avoir lu votre question, nous nous ressemblons beaucoup, et je me demande exactement la même chose depuis des années maintenant (depuis que j'ai entendu parler des cadres d'entités tierces, et maintenant de Microsoft)
pearcewg

1
Êtes-vous en train de dire que la logique métier est constituée des objets auxiliaires ou des processus stockés? Je demande que beaucoup de gens semblent penser que vous dites le plus tard ... mais ce que je pense que vous dites, c'est que vous avez toujours une logique métier dans les objets codés, vous obtenez simplement des données directement de la base de données et utilisez ces données , plutôt qu'un ORM ou un mappage vers des objets spécialisés pour contenir les données. J'ai tendance à ressentir la même chose - mais j'évalue également actuellement EF4 pour voir si cela en vaut la peine.
alchimique le

"Les consommateurs innovateurs sont souvent ceux qui se font foutre." - quelqu'un avec expérience
Uğur Gümüşhan

J'ai hérité d'un système avec plus de 2500 SPROC où l'application est considérée comme un simple moyen d'activer les SPROC et de donner un sens à leur sortie. Chaque donnée lue et écrite a son propre SPROC. Il n'y a pas de points de contrôle centraux. C'est hideux et à peu près aussi malléable que le granit. J'envisage d'optimiser les bases de données. Les 2500 SPROCS m'ont mis à ma place. Comparé à un système avec une couche de domaine bien organisée et un code DAL réutilisable, il semble mal conçu et est un cauchemar de support. Les tâches simples prennent des heures et détruisent l'âme. Les SPROC doivent être utilisés pour des charges élevées ou des méthodes spéciales IMO
trucker_jim

À propos de votre exemple de "débogage": avec les tests unitaires, vous saurez beaucoup plus rapidement où les choses vont mal.
MarioDS

Réponses:


59

Je pense que cela dépend de la complexité de la «logique» de l'application et de l'endroit où vous l'avez implémentée. Si toute votre logique est dans des procédures stockées et que votre application ne fait qu'appeler ces procédures et afficher les résultats, le développement d'objets d'entité est en effet une perte de temps. Mais pour une application où les objets ont des interactions riches les uns avec les autres, et la base de données n'est qu'un mécanisme de persistance, il peut être utile d'avoir ces objets.

Donc, je dirais qu'il n'y a pas de réponse unique. Les développeurs doivent être conscients que, parfois, essayer d'être trop OO peut causer plus de problèmes qu'il n'en résout.


Kristopher, il semble que vous ayez ressuscité cette question en y faisant un lien à partir d'une autre question. Je me demande ce que vous entendez par «interactions riches» et comment il serait impossible de les implémenter sans objets?
Eric Z Beard le

4
Tout ce qui peut être fait avec des objets peut également être fait sans objets. Je trouve que la conception OO est généralement beaucoup plus facile que les méthodes non-OO pour faire quelque chose de «compliqué», mais je comprends que cela ne fonctionne pas pour tout le monde.
Kristopher Johnson

Je suis d'accord - la réponse "quand utiliser les objets" dépend de la question de savoir si les propriétés des objets peuvent nécessiter des actions ou un changement de logique métier. Par exemple, les instances d'utilisateur ou de personne peuvent avoir un mot de passe et un nom de connexion -> vos actions de code changent en fonction de leurs valeurs. Au contraire, si vous aviez un produit, vous auriez fait afficher (rien de plus, pas d'autres actions) que d'obtenir un DataSet à partir de db et simplement construire l'interface graphique.
Yordan Georgiev

5
Il y a un équilibre. Évitez la religion et choisissez ce qui fonctionne.
Jeff Davis

27

La théorie dit que des implémentations hautement cohésives et faiblement couplées sont la voie à suivre.

Je suppose donc que vous remettez en question cette approche, à savoir la séparation des préoccupations.

Mon fichier aspx.cs doit-il interagir avec la base de données, appeler un sproc et comprendre IDataReader?

Dans un environnement d'équipe, en particulier lorsque vous avez des personnes moins techniques qui s'occupent de la partie aspx de l'application, je n'ai pas besoin que ces personnes soient capables de "toucher" ce truc.

Séparer mon domaine de ma base de données me protège des changements structurels dans la base de données, sûrement une bonne chose? Bien sûr, l'efficacité de la base de données est absolument importante, alors laissez quelqu'un qui est le plus excellent dans ce domaine s'occuper de cela, en un seul endroit, avec le moins d'impact possible sur le reste du système.

À moins que je ne comprenne mal votre approche, un changement structurel dans la base de données pourrait avoir un impact important sur la surface de votre application. Je vois que cette séparation des préoccupations me permet, à moi et à mon équipe, de minimiser cela. De plus, tout nouveau membre de l'équipe devrait mieux comprendre cette approche.

De plus, votre approche semble préconiser que la logique métier de votre application réside dans votre base de données? Cela me semble faux, SQL est vraiment bon pour interroger les données et non, à mon humble avis, pour exprimer la logique métier.

Pensée intéressante cependant, même si elle me semble à un pas de SQL dans l'aspx, qui, depuis mes mauvais jours asp non structurés, me remplit d'effroi.


Je suis d'accord que le fait d'avoir beaucoup de SQL dynamique saupoudré dans le code derrière est mal. Vous devez garder les appels Db clairs et distincts. L'emballage des appels sproc dans des méthodes d'assistance statiques permet une sorte de séparation sans aller jusqu'au bout de la route ORM.
Eric Z Beard le

1
Bien que je n'ai jamais travaillé dans un environnement asp, je suis sûr que certaines des personnes les moins techniques vous épateraient avec du code javascript côté client, ce qui se traduirait par une belle expérience utilisateur quelle que soit l'interface de merde à l'arrière technique. -fin.
crowne

Je suis d'accord avec vous ici et je suis connu pour faire du javascript côté client, ce qui a abouti à une expérience utilisateur pas trop minable, même si je le dis moi-même. J'aimerais penser que les interfaces back-end ne sont pas merdiques et que les programmeurs côté client n'ont pas à s'inquiéter de tout cela, car j'ai tenté de séparer mes préoccupations.
nachojammers

2
"Cela me semble faux, SQL est vraiment bon pour interroger les données et non, à mon humble avis, pour exprimer la logique métier." - Sauf si vous utilisez, par exemple, PL / SQL, qui ajoute un langage de programmation riche au-dessus (et étroitement intégré avec) le SQL, et votre code est stocké dans la base de données. Augmente les performances en évitant les allers-retours réseau. Et encapsule votre logique métier quel que soit le client qui se connecte à la base de données.
ObiWanKenobi

25

Une raison - séparer votre modèle de domaine de votre modèle de base de données.

Ce que je fais, c'est utiliser le développement piloté par les tests, donc j'écris d'abord mes couches d'interface utilisateur et de modèle et la couche de données est simulée, de sorte que l'interface utilisateur et le modèle sont construits autour d'objets spécifiques au domaine, puis plus tard, je mappe ces objets à la technologie que j'utilise la couche de données. C'est une mauvaise idée de laisser la structure de la base de données déterminer la conception de votre application. Dans la mesure du possible, écrivez d'abord l'application et laissez cela influencer la structure de votre base de données, et non l'inverse.


9
Je dois dire que je suis vraiment en désaccord, du moins pour les applications d'entreprise. Les données sont l'application.
Eric Z Beard le

3
pourquoi voudriez-vous avoir deux modèles distincts des mêmes données?
Seun Osewa

3
Parce qu'à certaines fins, une interprétation du modèle peut être plus appropriée. Certaines logiques fonctionnent beaucoup mieux sur les objets que sur les lignes.
Wouter Lievens

1
Je pense que c'est une bonne idée mal mise en œuvre.
Jeff Davis

21

Pour moi, cela revient à ne pas vouloir que mon application se préoccupe de la façon dont les données sont stockées. Je vais probablement être giflé pour avoir dit cela ... mais votre application ne sont pas vos données, les données sont un artefact de l'application. Je veux que mon application réfléchisse en termes de clients, commandes et articles, pas une technologie comme DataSets, DataTables et DataRows ... car qui sait combien de temps ils dureront.

Je conviens qu'il y a toujours une certaine quantité de couplage, mais je préfère que ce couplage atteigne vers le haut plutôt que vers le bas. Je peux modifier les branches et les feuilles d'un arbre plus facilement que de modifier son tronc.

J'ai tendance à réserver les sprocs pour les rapports car les requêtes ont tendance à être un peu plus méchantes que l'accès aux données générales des applications.

J'ai aussi tendance à penser qu'avec des tests unitaires appropriés au début de ce scénario, c'est comme si une colonne n'étant pas persistante ne sera probablement pas un problème.


3
"votre application ne sont pas vos données, les données sont un artefact de l'application." - L'application est sans valeur sans les données. Les données ont une grande valeur sans l'application. Les applications vont et viennent (se réécrivent) tout le temps, les données de toute application non triviale sont toujours conservées. Et le modèle de données reste étonnamment stable dans le temps.
ObiWanKenobi

16

Eric, tu es mort. Pour toute application vraiment évolutive / facile à entretenir / robuste, la seule vraie réponse est de se passer de toutes les ordures et de s'en tenir aux bases.

J'ai suivi une trajectoire similaire avec ma carrière et suis arrivé aux mêmes conclusions. Bien sûr, nous sommes considérés comme des hérétiques et regardés drôle. Mais mes affaires fonctionnent et fonctionnent bien.

Chaque ligne de code doit être considérée avec suspicion.


2
Bien sûr, cela fonctionne certainement bien lorsque vous avez des tonnes de personnel et de ressources, mais je pense que si vous êtes une équipe composée d'un seul homme, penser à de «nouvelles» techniques peut beaucoup aider.
Carl Hörberg

10

Je voudrais répondre par un exemple similaire à celui que vous avez proposé.

Dans mon entreprise, j'ai dû créer une simple section CRUD pour les produits, je construis toutes mes entités et un DAL séparé. Plus tard, un autre développeur a dû modifier une table associée et il a même renommé plusieurs champs. Le seul fichier que j'ai dû modifier pour mettre à jour mon formulaire était le DAL de cette table.

Ce que (à mon avis) les entités apportent à un projet est:

Ortogonalité: les changements dans une couche peuvent ne pas affecter les autres couches (bien sûr, si vous apportez un changement important à la base de données, cela se répercuterait sur toutes les couches, mais la plupart des petits changements ne le seront pas).

Testabilité: vous pouvez tester votre logique sans toucher à votre base de données. Cela augmente les performances de vos tests (vous permettant de les exécuter plus fréquemment).

Séparation des préoccupations: Dans un gros produit, vous pouvez attribuer la base de données à un DBA et il peut en optimiser l'enfer. Attribuez le modèle à un expert métier qui possède les connaissances nécessaires pour le concevoir. Attribuez des formulaires individuels à des développeurs plus expérimentés sur les formulaires Web, etc.

Enfin, j'aimerais ajouter que la plupart des mappeurs ORM prennent en charge les procédures stockées puisque c'est ce que vous utilisez.

À votre santé.


2
Les procédures stockées sont probablement le meilleur exemple d'orthogonalité et de séparation des préoccupations. S'ils sont utilisés correctement, ils encapsulent complètement la base de données.
Eric Z Beard

1
@Eric Z Beard: Oui, mais comment écrire des tests unitaires autour des procédures stockées tout en isolant uniquement la logique de la procédure stockée? Les procédures stockées sont étroitement couplées à la base de données, et la plupart d'entre nous, les types ORM n'aiment pas cela. Afin d'écrire un test unitaire pour une procédure stockée, vous devez vous fier à certaines données pour se trouver dans la base de données. et vous ne pouvez pas réexécuter ce test encore et encore sans cette dépendance de données. Ce test que vous écririez ne serait plus un test unitaire, mais plutôt un test d'intégration.
7wp

8

Je pense que vous «mordez plus que vous ne pouvez mâcher» sur ce sujet. Ted Neward n'était pas désinvolte lorsqu'il l'appelait le « Vietnam de l'informatique ».

Une chose que je peux absolument vous garantir est que cela ne changera le point de vue de personne sur la question, comme cela a été prouvé si souvent sur d'innombrables autres blogs, forums, podcasts, etc.

C'est certainement normal d'avoir une discussion ouverte et un débat sur un sujet controversé, c'est juste que celui-ci a été fait tellement de fois que les deux «côtés» ont accepté de ne pas être d'accord et se sont juste mis à écrire un logiciel.

Si vous souhaitez approfondir votre lecture des deux côtés, consultez les articles sur le blog de Ted, Ayende Rahein, Jimmy Nilson, Scott Bellware, Alt.Net, Stephen Forte, Eric Evans, etc.


1
Vous avez raison, la plupart des gens ne changeront pas leurs opinions. Je me rends compte que l'objectif de Stack Overflow est censé être des questions objectives, mais les questions subjectives sont tellement plus amusantes! Personnellement, j'ai beaucoup appris de cette discussion.
Eric Z Beard le

Je pense que les diverses approches sont spécifiques au contexte et que cette discussion peut servir à lever l'ambiguïté des scénarios qui bénéficient ou nuisent aux différents modèles de persistance. Les experts sur le sujet ne changeront pas d'avis rapidement, mais c'est un site de questions où les gens recherchent l'expérience des autres.
TheXenocide

Sensationnel. +1 pour le lien vers l'article "Vietnam of Computer Science" qui présente une excellente introduction au sujet ORM vs. non ORM.
lambacck

7

@Dan, désolé, ce n'est pas le genre de chose que je recherche. Je connais la théorie. Votre déclaration "est une très mauvaise idée" n'est pas étayée par un exemple réel. Nous essayons de développer des logiciels en moins de temps, avec moins de personnel, avec moins d'erreurs, et nous voulons pouvoir apporter des modifications facilement. Votre modèle multicouche, d'après mon expérience, est négatif dans toutes les catégories ci-dessus. Surtout pour faire du modèle de données la dernière chose que vous faites. Le modèle de données physiques doit être une considération importante dès le premier jour.


wow, quelqu'un d'autre qui pense comme moi à ce sujet ... mes applications concernent presque toujours la manipulation de données, c'est ce qu'elles font réellement.
alchimique le

Ce serait mieux comme commentaire maintenant que ces fonctionnalités sont disponibles
Casebash

1
Pardonnez-moi d'être pédant, mais comme c'est une question populaire et que l'erreur que vous avez commise est courante, j'ai eu envie de la signaler. «Moins de temps» est correct, mais «moins de gens» et «moins d'erreurs» devraient être «moins de gens» et «moins d'erreurs». Si vous avez moins de farine, vous pouvez faire moins de biscuits. (De plus, si vous utilisez trop de farine, vous ferez trop de biscuits - une distinction moins souvent oubliée.) Encore une fois, mes excuses; J'essaie seulement d'aider.
WCWedin le

4

J'ai trouvé votre question vraiment intéressante.
En général, j'ai besoin d'objets entités pour encapsuler la logique métier d'une application. Ce serait vraiment compliqué et inadéquat de pousser cette logique dans la couche de données.
Que feriez-vous pour éviter ces objets entités? Quelle solution avez-vous en tête?


Ce serait mieux comme commentaire
Casebash

4

Les objets d'entité peuvent faciliter la mise en cache sur la couche d'application. Bonne chance pour la mise en cache d'un lecteur de données.


4

Nous devrions également parler de la notion de ce que sont réellement les entités. Quand je lis cette discussion, j'ai l'impression que la plupart des gens ici regardent les entités dans le sens d'un modèle de domaine anémique . Beaucoup de gens considèrent le modèle de domaine anémique comme un anti-modèle!

Il y a de la valeur dans les modèles de domaines riches. C'est ce qu'est le Domain Driven Design . Je pense personnellement que l'OO est un moyen de conquérir la complexité. Cela signifie non seulement une complexité technique (comme l'accès aux données, l'interface utilisateur, la sécurité ...) mais aussi une complexité dans le domaine métier !

Si nous pouvons appliquer des techniques OO pour analyser, modéliser, concevoir et implémenter nos problèmes métier, c'est un énorme avantage pour la maintenabilité et l'extensibilité des applications non triviales!

Il existe des différences entre vos entités et vos tables. Les entités doivent représenter votre modèle, les tableaux ne représentent que l'aspect données de votre modèle!

Il est vrai que les données vivent plus longtemps que les applications, mais considérez cette citation de David Laribee : Les modèles sont éternels ... les données sont un effet secondaire heureux.

Quelques liens supplémentaires sur ce sujet:


1
Franchement, je commence à croire que les données vivent plus longtemps que les logiciels qui les entourent, car souvent si peu d'attention est accordée à la conception du logiciel avec une véritable compréhension de l'entreprise.
flq

4

Question vraiment intéressante. Honnêtement, je ne peux pas prouver pourquoi les entités sont bonnes. Mais je peux partager mon opinion sur les raisons pour lesquelles je les aime. Code comme

void exportOrder(Order order, String fileName){...};

ne concerne pas la provenance de l'ordre - de la base de données, de la requête Web, du test unitaire, etc. Cela rend cette méthode plus explicitement déclarée exactement ce qu'elle nécessite, au lieu de prendre DataRow et de documenter les colonnes qu'elle s'attend à avoir et les types qu'elles devraient être. Il en va de même si vous l'implémentez d'une manière ou d'une autre en tant que procédure stockée - vous devez toujours lui envoyer l'ID d'enregistrement, alors qu'il n'est pas nécessaire qu'il soit présent dans la base de données.

La mise en œuvre de cette méthode serait effectuée sur la base de l'abstraction Order, et non sur la façon dont elle est présentée exactement dans DB. La plupart de ces opérations que j'ai implémentées ne dépendent vraiment pas de la façon dont ces données sont stockées. Je comprends que certaines opérations nécessitent un couplage avec la structure DB à des fins de performances et d'évolutivité, juste d'après mon expérience, il n'y en a pas trop. D'après mon expérience, très souvent, il suffit de savoir que Person a .getFirstName () renvoie String, et .getAddress () renvoie l'adresse, et que l'adresse a .getZipCode (), etc. - et ne se soucie pas des tables impliquées pour stocker ces données .

Si vous devez faire face à des problèmes comme vous l'avez décrit, comme lorsque la colonne supplémentaire rompt les performances du rapport, alors pour vos tâches, la base de données est une partie critique, et vous devriez en effet en être aussi proche que possible. Bien que les entités puissent fournir des abstractions pratiques, elles peuvent également masquer certains détails importants.

L'évolutivité est un point intéressant ici - la plupart des sites Web qui nécessitent une énorme évolutivité (comme facebook, livejournal, flickr) ont tendance à utiliser l'approche DB-ascétique, lorsque DB est utilisé aussi rare que possible et que les problèmes d'évolutivité sont résolus par la mise en cache, en particulier par l'utilisation de la RAM. http://highscalability.com/ contient des articles intéressants à ce sujet.


2
L'évolutivité dans les applications d'entreprise n'est pas souvent résoluble par la mise en cache, car une grande partie de celle-ci modifie fréquemment les données transactionnelles sur des tables comportant des millions de lignes. Je vois facebook et. Al. en tant que sites Web à grand volume, où le plus difficile est de répondre à tant de requêtes Web.
Eric Z Beard

4

Il existe d'autres bonnes raisons pour les objets d'entité en plus de l'abstraction et du couplage lâche. L'une des choses que j'aime le plus est la frappe forte que vous ne pouvez pas obtenir avec un DataReader ou un DataTable. Une autre raison est que lorsqu'elles sont bien faites, les classes d'entités appropriées peuvent rendre le code plus maintenable en utilisant des constructions de première classe pour les termes spécifiques au domaine que quiconque regarde le code est susceptible de comprendre plutôt qu'un tas de chaînes avec des noms de champ utilisés. pour indexer un DataRow. Les procédures stockées sont vraiment orthogonales à l'utilisation d'un ORM car de nombreux frameworks de mappage vous donnent la possibilité de mapper sur des sprocs.

Je ne considérerais pas les sprocs + datareaders comme un substitut à un bon ORM. Avec les procédures stockées, vous êtes toujours contraint et étroitement couplé à la signature de type de la procédure, qui utilise un système de type différent du code appelant. Les procédures stockées peuvent être sujettes à modification pour prendre en charge des options supplémentaires et des changements de schéma. Une alternative aux procédures stockées dans le cas où le schéma est sujet à modification consiste à utiliser des vues - vous pouvez mapper des objets à des vues, puis remapper des vues aux tables sous-jacentes lorsque vous les modifiez.

Je peux comprendre votre aversion pour les ORM si votre expérience consiste principalement en Java EE et CSLA. Vous voudrez peut-être jeter un coup d'œil à LINQ to SQL, qui est un cadre très léger et est principalement un mappage un-à-un avec les tables de base de données, mais qui n'a généralement besoin que d'une extension mineure pour qu'ils deviennent des objets métier à part entière. LINQ to SQL peut également mapper des objets d'entrée et de sortie aux paramètres et résultats des procédures stockées.

La structure d'entité ADO.NET présente l'avantage supplémentaire que vos tables de base de données peuvent être affichées comme des classes d'entité héritant les unes des autres, ou comme des colonnes de plusieurs tables agrégées en une seule entité. Si vous devez modifier le schéma, vous pouvez modifier le mappage du modèle conceptuel vers le schéma de stockage sans modifier le code d'application réel. Et encore une fois, les procédures stockées peuvent être utilisées ici.

Je pense que plus de projets informatiques dans les entreprises échouent en raison d'une non-maintenabilité du code ou d'une faible productivité des développeurs (qui peut provenir, par exemple, d'un changement de contexte entre l'écriture de sproc et l'écriture d'applications) que des problèmes d'évolutivité d'une application.


Je pense qu'un bon compromis serait de mapper un ORM sur des procédures stockées, sauf que cela peut facilement être mal fait: si vous créez simplement les 4 processus CRUD pour chaque table, vous n'avez rien accompli. Pouvez-vous mapper de gros processus à gros grains sur des entités, ou cela ne vous mène-t-il pas vraiment à quelque chose?
Eric Z Beard

En plus des opérations CRUD, les ORM Microsoft vous permettent d'ajouter des méthodes sur les classes d'entités qui mappent directement à tout processus stocké que vous souhaitez lui lancer (à condition que tous les types d'entrée / sortie soient mappables).
Mark Cidade le

3

Je voudrais également ajouter à la réponse de Dan que la séparation des deux modèles pourrait permettre à votre application d'être exécutée sur différents serveurs de base de données ou même sur des modèles de base de données.


3

Que faire si vous avez besoin de mettre à l'échelle votre application en équilibrant la charge de plusieurs serveurs Web? Vous pouvez installer l'application complète sur tous les serveurs Web, mais une meilleure solution consiste à faire en sorte que les serveurs Web parlent à un serveur d'applications.

Mais s'il n'y a pas d'objets d'entité, ils n'auront pas grand chose à dire.

Je ne dis pas que vous ne devriez pas écrire de monolithes s'il s'agit d'une application simple, interne et de courte durée. Mais dès que cela devient moyennement complexe, ou que cela devrait durer beaucoup de temps, vous devez vraiment penser à un bon design.

Cela permet de gagner du temps lors de sa maintenance.

En séparant la logique d'application de la logique de présentation et de l'accès aux données, et en passant des DTO entre eux, vous les découplez. Leur permettre de changer indépendamment.


3
Beaucoup de gens évoquent le découplage et permettent à une couche de changer sans affecter l'autre. Les procédures stockées le font mieux que n'importe quel ORM! Je peux modifier radicalement le modèle de données, et tant que les procédures renvoient les mêmes données, rien ne casse.
Eric Z Beard le

2
À mon avis, les procédures stockées ET un modèle d'entité ne s'excluent pas mutuellement. Les procédures stockées peuvent fournir un mécanisme pour stocker votre modèle d'entité. La question est la suivante: votre logique métier fonctionne-t-elle avec les entités ou accède-t-elle directement aux procédures stockées?
jbandi

3

Vous pourriez trouver ce post sur comp.object intéressant.

Je ne prétends pas être d'accord ou en désaccord mais c'est intéressant et (je pense) pertinent à ce sujet.


C'est un excellent article. Résume presque parfaitement mes pensées sur les ORM.
Eric Z Beard

3

Une question: comment gérez-vous les applications déconnectées si toute votre logique métier est piégée dans la base de données?

Dans le type d'application d'entreprise qui m'intéresse, nous devons traiter plusieurs sites, certains d'entre eux doivent pouvoir fonctionner dans un état déconnecté.
Si votre logique métier est encapsulée dans une couche de domaine qui est simple à incorporer dans divers types d'applications - disons, en tant que dll- alors je peux créer des applications qui connaissent les règles métier et sont capables, si nécessaire, de les appliquer localement.

En conservant la couche Domaine dans les procédures stockées sur la base de données, vous devez vous en tenir à un seul type d'application qui a besoin d'une ligne de vue permanente sur la base de données.

C'est correct pour une certaine classe d'environnements, mais cela ne couvre certainement pas tout le spectre des applications d'entreprise .


2

@jdecuyper, une maxime que je me répète souvent est "si votre logique métier n'est pas dans votre base de données, ce n'est qu'une recommandation". Je pense que Paul Nielson a dit cela dans l'un de ses livres. Les couches d'application et l'interface utilisateur vont et viennent, mais les données durent généralement très longtemps.

Comment éviter les objets entité? Procédures stockées principalement. J'admets aussi librement que la logique métier a tendance à atteindre toutes les couches d'une application, que vous en ayez l'intention ou non. Un certain couplage est inhérent et inévitable.


Je suis d'accord, la logique métier qui se trouve dans l'application ne tient souvent pas compte des autres façons dont les données peuvent être saisies, supprimées ou modifiées. Cela entraîne généralement des problèmes d'intégrité des données sur la route.
HLGEM

Et pourquoi vous devriez toujours utiliser une couche de service pour gérer le décalage entre le monde objet et le monde relationnel. La logique métier qui se répand à chaque couche n'est certainement PAS inévitable.
cdaq

2

J'ai beaucoup réfléchi à la même chose ces derniers temps; J'ai été un grand utilisateur de CSLA pendant un certain temps, et j'aime la pureté de dire que "toute votre logique métier (ou du moins autant que cela est raisonnablement possible) est encapsulée dans des entités commerciales".

J'ai vu le modèle d'entité commerciale apporter beaucoup de valeur dans les cas où la conception de la base de données est différente de la façon dont vous travaillez avec les données, ce qui est le cas dans de nombreux logiciels d'entreprise.

Par exemple, l'idée d'un «client» peut consister en un enregistrement principal dans une table Client, combiné avec toutes les commandes que le client a passées, ainsi que tous les employés du client et leurs coordonnées, et certaines des propriétés de un client et ses enfants peuvent être déterminés à partir des tables de recherche. C'est vraiment bien du point de vue du développement de pouvoir travailler avec le client en tant qu'entité unique, car d'un point de vue commercial, le concept de client contient toutes ces choses et les relations peuvent ou non être appliquées dans la base de données.

Bien que j'apprécie la citation selon laquelle "si votre règle métier n'est pas dans votre base de données, ce n'est qu'une suggestion", je pense également que vous ne devriez pas concevoir la base de données pour appliquer les règles métier, vous devez la concevoir pour qu'elle soit efficace, rapide et normalisée .

Cela dit, comme d'autres l'ont noté ci-dessus, il n'y a pas de «conception parfaite», l'outil doit s'adapter au travail. Mais l'utilisation d'entités commerciales peut vraiment aider à la maintenance et à la productivité, car vous savez où aller pour modifier la logique métier, et les objets peuvent modéliser des concepts réels de manière intuitive.


2

Eric,

Personne ne vous empêche de choisir le cadre / l'approche que vous souhaiteriez. Si vous allez suivre le chemin "piloté par les données / basé sur les procédures stockées", alors allez-y! Surtout si cela vous aide vraiment à livrer vos applications selon les spécifications et à temps.

La mise en garde étant (un revers à votre question), TOUTES vos règles métier doivent être sur des procédures stockées, et votre application n'est rien de plus qu'un client léger.

Cela étant dit, les mêmes règles s'appliquent si vous faites votre application en POO: soyez cohérent. Suivez les principes de la POO, et cela inclut la création d'objets d'entité pour représenter vos modèles de domaine.

La seule vraie règle ici est le mot cohérence . Personne ne vous empêche de vous concentrer sur DB. Personne ne vous empêche de suivre des programmes structurés à l'ancienne (c'est-à-dire fonctionnels / procéduraux). Merde, personne n'empêche quiconque de faire du code de style COBOL. MAIS une application doit être très, très cohérente une fois sur cette voie, si elle souhaite atteindre un degré de succès.


Je suis d'accord avec la cohérence dans toute l'application. Pour être honnête, j'ai changé de direction sur mon projet actuel il y a quelque temps et je n'ai jamais réussi à réparer à 100% le modèle d'origine, ce qui rend les choses confuses. Les bonnes décisions sont mieux prises tôt.
Eric Z Beard

Eric, c'est vrai. Une fois, j'étais un fanatique de la POO (comme les autres dans ce fil semblent l'être), mais j'ai rencontré un gars qui possède une entreprise qui vend très bien des applications basées sur la base de données. Cela a secoué mon monde. Je suis toujours un mordu de la POO / TDD mais je ne fronce plus les sourcils sur DB-centric.
Jon Limjap

Le problème est que parfois les gens vendent trop leur idéologie, vous pourriez potentiellement gagner votre vie en vendant uniquement des sites html et javascript, si vous aviez une bonne méthodologie pour les lancer.
Mark Rogers

2

Je ne suis vraiment pas sûr de ce que vous considérez comme des «applications d'entreprise». Mais j'ai l'impression que vous le définissez comme une application interne où le SGBDR serait gravé dans le marbre et le système n'aurait pas à être interopérable avec d'autres systèmes, qu'ils soient internes ou externes.

Mais que se passe-t-il si vous aviez une base de données avec 100 tables, ce qui équivaut à 4 procédures stockées pour chaque table uniquement pour les opérations CRUD de base, soit 400 procédures stockées qui doivent être maintenues et ne sont pas fortement typées, donc sensibles aux fautes de frappe et ne peuvent pas être testées à l'unité . Que se passe-t-il lorsque vous obtenez un nouveau CTO qui est un évangéliste Open Source et veut changer le SGBDR de SQL Server à MySql?

Aujourd'hui, de nombreux logiciels, qu'il s'agisse d'applications ou de produits d'entreprise, utilisent SOA et ont certaines exigences pour exposer les services Web, du moins les logiciels que je suis et avec lesquels j'ai été impliqué. En utilisant votre approche, vous finiriez par exposer un DataTable ou DataRows sérialisé. Maintenant, cela peut être considéré comme acceptable si le client est garanti .NET et sur un réseau interne. Mais lorsque le client n'est pas connu, vous devez vous efforcer de concevoir une API intuitive et dans la plupart des cas, vous ne voudriez pas exposer le schéma de base de données complète. Je ne voudrais certainement pas expliquer à un développeur Java ce qu'est un DataTable et comment l'utiliser. Il y a aussi la prise en compte de la bande passante et de la taille de la charge utile et des DataTables sérialisés, les DataSets sont très lourds.

Il n'y a pas de solution miracle avec la conception de logiciels et cela dépend vraiment des priorités, pour moi, c'est dans un code testable à l'unité et des composants faiblement couplés qui peuvent être facilement utilisés par n'importe quel client.

juste mes 2 cents


Non, ma définition de l'application d'entreprise est le contraire. Le schéma change souvent et de nombreuses applications utilisent la base de données et interagissent avec de nombreux partenaires externes. Dans une vraie application d'entreprise, vous ne passerez jamais, jamais, jamais à un autre SGBDR. Cela n'arrive tout simplement pas.
Eric Z Beard

Et créer 4 processus pour chaque table est une mauvaise pratique. Il vous couple étroitement au modèle de données, tout comme le SQL généré à partir d'un ORM, de sorte qu'il ne vous achète rien. Les processus doivent être des opérations commerciales à gros grains, pas seulement des CRUD sur chaque table.
Eric Z Beard

Mais n'est-ce pas la réponse?: Plus vous avez besoin d'écrire de code, plus vous avez besoin de fonctionnalités pour la prise en charge de la programmation à grande échelle: encapsulation, saisie de chaînes, refactoring, style sophistiqué et vérification des erreurs, etc. Java et .NET sont très pris en charge dans ce domaine, contrairement aux langages de procédure stockée.
reinierpost le

2

Je voudrais offrir un autre angle au problème de la distance entre OO et RDB: l'histoire.

Tout logiciel a un modèle de réalité qui est dans une certaine mesure une abstraction de la réalité. Aucun programme informatique ne peut capturer toutes les complexités de la réalité, et les programmes sont écrits uniquement pour résoudre un ensemble de problèmes à partir de la réalité. Par conséquent, tout modèle logiciel est une réduction de la réalité. Parfois, le modèle logiciel force la réalité à se réduire. Par exemple, lorsque vous souhaitez que la société de location de voitures vous réserve une voiture tant qu'elle est bleue et qu'elle contient des alliages, mais que l'opérateur ne peut pas se conformer car votre demande ne rentre pas dans l'ordinateur.

RDB est issu d'une très ancienne tradition de mise en tables d'informations, appelée comptabilité. La comptabilité se faisait sur papier, puis sur cartes perforées, puis sur ordinateur. Mais la comptabilité est déjà une réduction de la réalité. La comptabilité a forcé les gens à suivre son système depuis si longtemps qu'il est devenu une réalité acceptée. C'est pourquoi il est relativement facile de créer un logiciel informatique pour la comptabilité, la comptabilité a eu son modèle d'information, bien avant l'arrivée de l'ordinateur.

Compte tenu de l'importance de bons systèmes comptables et de l'acceptation que vous obtenez de tous les chefs d'entreprise, ces systèmes sont devenus très avancés. Les bases de la base de données sont désormais très solides et personne n'hésite à conserver des données vitales dans quelque chose d'aussi fiable.

Je suppose que OO a dû arriver lorsque les gens ont découvert que d'autres aspects de la réalité sont plus difficiles à modéliser que la comptabilité (qui est déjà un modèle). OO est devenu une idée très réussie, mais la persistance des données OO est relativement sous-développée. RDB / Comptabilité a eu des gains faciles, mais OO est un domaine beaucoup plus vaste (essentiellement tout ce qui n'est pas comptable).

Beaucoup d'entre nous ont voulu utiliser OO, mais nous voulons toujours un stockage sécurisé de nos données. Quoi de plus sûr que de stocker nos données de la même manière que le fait le système comptable estimé? C'est une perspective séduisante, mais nous nous heurtons tous aux mêmes écueils. Très peu ont pris la peine de penser à la persistance OO par rapport aux efforts massifs de l'industrie RDB, qui a bénéficié de la tradition et de la position de la comptabilité.

Prevayler et db4o sont quelques suggestions, je suis sûr qu'il y en a d'autres dont je n'ai pas entendu parler, mais aucune n'a semblé obtenir la moitié de la presse comme, par exemple, l'hibernation.

Le stockage de vos objets dans de bons vieux fichiers ne semble même pas être pris au sérieux pour les applications multi-utilisateurs, et en particulier les applications Web.

Dans ma lutte quotidienne pour fermer le gouffre entre OO et RDB, j'utilise OO autant que possible, mais j'essaie de minimiser l'héritage. Je n'utilise pas souvent des SP. J'utiliserai les requêtes avancées uniquement dans les aspects qui ressemblent à la comptabilité.

Je serai heureusement surpris quand le gouffre sera fermé pour de bon. Je pense que la solution viendra quand Oracle lancera quelque chose comme "Oracle Object Instance Base". Pour vraiment faire son chemin, il devra avoir un nom rassurant.


Je ne pense pas que vous ayez besoin d'ORM pour que OO soit considéré comme utile. J'utilise des procs stockés et j'écris beaucoup de classes d'assistance statiques dans mon code, mais ces classes sont construites sur l'énorme framework .NET, qui est une fantastique collection d'objets.
Eric Z Beard

Votre logique a du sens, mais je ne pense pas que la prémisse soit valable. Je n'ai jamais entendu parler de quoi que ce soit qui ne puisse pas être mappé avec RDB.
Jeff Davis le

1

Pas beaucoup de temps pour le moment, mais juste au-dessus de ma tête ...

Le modèle d'entité vous permet de donner une interface cohérente à la base de données (et à d'autres systèmes possibles) même au-delà de ce qu'une interface de procédure stockée peut faire. En utilisant des modèles commerciaux à l'échelle de l'entreprise, vous pouvez vous assurer que toutes les applications affectent les données de manière cohérente, ce qui est TRÈS important. Sinon, vous vous retrouvez avec de mauvaises données, ce qui est tout simplement un mal.

Si vous n'avez qu'une seule application, vous n'avez pas vraiment de système «d'entreprise», quelle que soit la taille de cette application ou de vos données. Dans ce cas, vous pouvez utiliser une approche similaire à celle dont vous parlez. Soyez simplement conscient du travail qui sera nécessaire si vous décidez de développer vos systèmes à l'avenir.

Voici cependant quelques points à garder à l'esprit (IMO):

  1. Le code SQL généré est incorrect (exceptions à suivre). Désolé, je sais que beaucoup de gens pensent que c'est un gain de temps énorme, mais je n'ai jamais trouvé de système qui pourrait générer un code plus efficace que ce que je pourrais écrire et souvent le code est tout simplement horrible. Vous finissez également souvent par générer une tonne de code SQL qui n'est jamais utilisé. L'exception ici concerne les modèles très simples, comme peut-être les tables de consultation. Cependant, beaucoup de gens se laissent emporter.
  2. Entités <> Tables (ou même des entités de modèle de données logiques nécessairement). Un modèle de données a souvent des règles de données qui doivent être appliquées aussi étroitement que possible à la base de données, ce qui peut inclure des règles sur la manière dont les lignes de table sont liées les unes aux autres ou d'autres règles similaires qui sont trop complexes pour l'IR déclarative. Ceux-ci doivent être traités dans des procédures stockées. Si toutes vos procédures stockées sont de simples processus CRUD, vous ne pouvez pas le faire. De plus, le modèle CRUD crée généralement des problèmes de performances car il ne minimise pas les allers-retours sur le réseau vers la base de données. C'est souvent le plus gros goulot d'étranglement dans une application d'entreprise.

Convenu du SQL généré. Cela cause toujours plus de problèmes qu'il n'en résout. Et je suis tout à fait contre la simple création d'une couche CRUD avec des processus stockés. Le processus doit être aussi grossier que possible. Je ne sais pas comment vous définissez «une application».
Eric Z Beard

Par une application, j'entends une seule application écrite par un seul groupe de l'organisation. Là où je consulte actuellement, ils ont une base de données d'entreprise à laquelle au moins trois groupes distincts travaillent sur trois applications différentes avec une communication limitée entre eux.
Tom H

1

Parfois, votre application et votre couche de données ne sont pas si étroitement couplées. Par exemple, vous pouvez avoir une application de facturation téléphonique. Vous créez plus tard une application distincte qui surveille l'utilisation du téléphone pour a) mieux vous annoncer b) optimiser votre forfait téléphonique.

Ces applications ont des préoccupations et des exigences de données différentes (même les données proviennent de la même base de données), elles conduiraient à des conceptions différentes. Votre base de code peut finir par un désordre absolu (dans l'une ou l'autre application) et un cauchemar à maintenir si vous laissez la base de données piloter le code.


1

Les applications dont la logique de domaine est séparée de la logique de stockage de données sont adaptables à tout type d'application de source de données (base de données ou autre) ou d'interface utilisateur (Web ou Windows (ou Linux, etc.)).

Vous êtes plutôt coincé dans votre base de données, ce qui n'est pas mal si vous êtes avec une entreprise qui est satisfaite du système de base de données actuel que vous utilisez. Cependant, étant donné que les bases de données évoluent au fil du temps, votre entreprise voudra peut-être utiliser un nouveau système de base de données vraiment soigné et nouveau. Et s'ils voulaient passer à une méthode d'accès aux données de services Web (comme le fait parfois l'architecture orientée services). Vous devrez peut-être porter vos procédures stockées partout.

De plus, la logique du domaine fait abstraction de l'interface utilisateur, ce qui peut être plus important dans les grands systèmes complexes qui ont des interfaces utilisateur en constante évolution (en particulier lorsqu'ils recherchent constamment plus de clients).

Aussi, même si je suis d'accord qu'il n'y a pas de réponse définitive à la question des procédures stockées par rapport à la logique de domaine. Je suis dans le camp de la logique de domaine (et je pense qu'ils gagnent avec le temps), car je pense que les procédures stockées élaborées sont plus difficiles à maintenir que la logique de domaine élaborée. Mais c'est un tout autre débat


0

Je pense que vous avez juste l'habitude d'écrire un type d'application spécifique et de résoudre un certain type de problème. Vous semblez attaquer cela du point de vue de la "base de données d'abord". Il existe de nombreux développeurs où les données sont conservées dans une base de données, mais les performances ne sont pas une priorité absolue. Dans de nombreux cas, mettre une abstraction sur la couche de persistance simplifie considérablement le code et le coût des performances n'est pas un problème.

Quoi que vous fassiez, ce n'est pas la POO. Ce n'est pas faux, ce n'est tout simplement pas de la POO, et il n'est pas logique d'appliquer vos solutions à tous les autres problèmes.


Les données viennent toujours en premier. C'est la raison pour laquelle vous avez le programme informatique en premier lieu. La "base de données d'abord" est donc probablement la seule approche valable pour la conception d'applications.
gbjbaanb

0

Question interessante. Quelques réflexions:

  1. Comment feriez-vous un test unitaire si toute votre logique métier était dans votre base de données?
  2. Les modifications apportées à la structure de votre base de données, en particulier celles qui affectent plusieurs pages de votre application, ne seraient-elles pas un problème majeur à changer dans l'application?

0

Bonne question!

Une approche que j'aime plutôt est de créer un objet itérateur / générateur qui émet des instances d'objets qui sont pertinents pour un contexte spécifique. Habituellement, cet objet encapsule des éléments d'accès à la base de données sous-jacents, mais je n'ai pas besoin de le savoir lors de son utilisation.

Par exemple,

Un objet AnswerIterator génère des objets AnswerIterator.Answer. Sous le capot, il itère une instruction SQL pour récupérer toutes les réponses et une autre instruction SQL pour récupérer tous les commentaires associés. Mais lors de l'utilisation de l'itérateur, j'utilise simplement l'objet Answer qui a les propriétés minimales pour ce contexte. Avec un peu de code squelette, cela devient presque trivial.

J'ai trouvé que cela fonctionne bien lorsque j'ai un énorme ensemble de données sur lequel travailler, et quand c'est bien fait, cela me donne de petits objets transitoires qui sont relativement faciles à tester.

C'est fondamentalement un placage mince sur le truc d'accès à la base de données, mais cela me donne toujours la flexibilité de l'abstraction quand j'en ai besoin.


0

Les objets de mes applications ont tendance à être liés un à un à la base de données, mais je trouve que l'utilisation de Linq To Sql plutôt que des sprocs facilite beaucoup l'écriture de requêtes compliquées, en particulier la possibilité de les créer à l'aide de l'exécution différée. par exemple de r dans Images.User.Ratings où etc. Cela m'évite d'essayer de travailler sur plusieurs instructions de jointure dans SQL, et avoir Skip & Take pour la pagination simplifie également le code plutôt que d'avoir à incorporer le row_number & 'over' code.


Il y a un grand danger à faire les choses de cette façon. La plupart des requêtes complexes doivent être entièrement réécrites par un DBA pour les mettre à l'échelle. Aucun réglage d'index ne peut faire ce que la modification d'une requête peut parfois faire. Ce type de Linq2Sql est un couplage extrêmement serré.
Eric Z Beard

0

Pourquoi s'arrêter aux objets d'entité? Si vous ne voyez pas la valeur avec les objets d'entité dans une application de niveau entreprise, accédez simplement à vos données dans un langage purement fonctionnel / procédural et connectez-le à une interface utilisateur. Pourquoi ne pas simplement découper tous les "peluches" OO?


Je ne vois pas OO comme "duvet". C'est juste qu'au cours de la dernière décennie, MSFT, Sun, etc. ont écrit 99% des objets dont nous aurons besoin. Ce n'est pas parce que j'écris beaucoup de classes statiques au-dessus du framework que je n'utilise pas OO.
Eric Z Beard
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.