Ermöglichen ORMs die Erstellung umfangreicher Domänenmodelle?

21

Nachdem ich Hibernate für die meisten meiner Projekte ungefähr 8 Jahre lang verwendet habe, bin ich bei einem Unternehmen gelandet, das von seiner Verwendung abrät und möchte, dass Anwendungen nur über gespeicherte Prozeduren mit der Datenbank interagieren.

Nachdem ich dies einige Wochen lang getan habe, konnte ich kein umfassendes Domänenmodell der Anwendung erstellen, die ich gerade erst zu erstellen beginne, und die Anwendung sieht nur aus wie ein (schreckliches) Transaktionsskript.

Einige der Probleme, die ich gefunden habe, sind:

  • Das Objektdiagramm kann nicht navigiert werden, da die gespeicherten Prozeduren nur die minimale Datenmenge laden. Dies bedeutet, dass manchmal ähnliche Objekte mit unterschiedlichen Feldern vorhanden sind. Ein Beispiel ist: Wir haben eine gespeicherte Prozedur, um alle Daten von einem Kunden abzurufen, und eine andere, um Kontoinformationen sowie einige Felder vom Kunden abzurufen.
  • Ein Großteil der Logik endet in Hilfsklassen, sodass der Code strukturierter wird (mit Entitäten, die als alte C-Strukturen verwendet werden).
  • Langweiliger Gerüstcode, da es kein Framework gibt, das Ergebnismengen aus einer gespeicherten Prozedur extrahiert und in eine Entität einfügt.

Meine Fragen sind:

  • Hat sich jemand in einer ähnlichen Situation befunden und war nicht mit der Vorgehensweise beim Laden einverstanden? was hast du getan?
  • Gibt es einen tatsächlichen Vorteil bei der Verwendung gespeicherter Prozeduren? abgesehen von dem dummen Punkt "Niemand kann eine Drop-Tabelle ausstellen".
  • Gibt es eine Möglichkeit, mithilfe gespeicherter Prozeduren eine Rich-Domain zu erstellen? Ich weiß, dass es die Möglichkeit gibt, mithilfe von AOP DAOs / Repositorys in Entitäten einzufügen, um im Objektdiagramm navigieren zu können. Ich mag diese Option nicht, da sie sehr nahe an Voodoo liegt.

Fazit

Zunächst einmal vielen Dank für Ihre Antworten. Die Schlussfolgerung, die ich gezogen habe, ist, dass ORMs nicht die Erstellung von Rich Domain-Modellen ermöglichen (wie einige Leute erwähnt haben), aber den Umfang der (oft wiederholten) Arbeit vereinfachen. Das Folgende ist eine detailliertere Erläuterung der Schlussfolgerung, basiert jedoch nicht auf harten Daten.

Die meisten Anwendungen fordern Informationen an und senden sie an andere Systeme. Dazu erstellen wir eine Abstraktion in den Modellbegriffen (z. B. ein Geschäftsereignis) und das Domänenmodell sendet oder empfängt das Ereignis. Das Ereignis benötigt normalerweise eine kleine Teilmenge an Informationen aus dem Modell, jedoch nicht das gesamte Modell. Beispielsweise fordert ein Zahlungsgateway in einem Online-Shop einige Benutzerinformationen und den Gesamtbetrag an, um einem Benutzer eine Gebühr in Rechnung zu stellen, benötigt jedoch nicht den Kaufverlauf, die verfügbaren Produkte und den gesamten Kundenstamm. Die Veranstaltung hat also einen kleinen und spezifischen Datensatz.

Wenn wir die Datenbank einer Anwendung als externes System verwenden, müssen wir eine Abstraktion erstellen, die es uns ermöglicht, die Domänenmodell- Entitäten der Datenbank zuzuordnen ( wie NimChimpsky erwähnt hat , unter Verwendung eines Daten-Mappers). Der offensichtliche Unterschied besteht darin, dass wir jetzt eine Zuordnung für jede Modellentität zur Datenbank (entweder ein Legacy-Schema oder gespeicherte Prozeduren) von Hand erstellen müssen, mit dem zusätzlichen Aufwand, dass eine Domänenentität teilweise zugeordnet werden kann, da die beiden nicht synchron sind einer Datenbankentität zugeordnet werden (z. B. wird eine UserCredentials-Klasse, die nur Benutzername und Kennwort enthält, einer Users-Tabelle mit anderen Spalten zugeordnet), oder eine Domänenmodellentität kann mehreren Datenbankentitäten zugeordnet werden (z. eine Zuordnung auf dem Tisch, aber wir wollen alle Daten in nur einer Klasse).

In einer Anwendung mit einigen wenigen Entitäten kann der zusätzliche Arbeitsaufwand gering sein, wenn die Entitäten nicht transversiert werden müssen. Er erhöht sich jedoch, wenn die Entitäten unter bestimmten Bedingungen transversiert werden müssen (und daher möchten wir möglicherweise eine Art 'Lazy' implementieren Wird geladen'). Mit zunehmender Anzahl von Entitäten in einer Anwendung nimmt diese Arbeit nur zu (und ich habe das Gefühl, dass sie nicht linear zunimmt). Ich gehe hier davon aus, dass wir nicht versuchen, ein ORM neu zu erfinden.

Ein Vorteil der Behandlung der Datenbank als externes System besteht darin, dass wir Situationen umgehen können, in denen zwei verschiedene Versionen einer Anwendung ausgeführt werden sollen, wobei jede Anwendung eine andere Zuordnung hat. Dies wird im Szenario kontinuierlicher Lieferungen an die Produktion interessanter ... aber ich denke, dass dies in geringerem Maße auch mit ORMs möglich ist.

Ich werde den Sicherheitsaspekt verwerfen, auf der Grundlage, dass ein Entwickler, auch wenn er keinen Zugriff auf die Datenbank hat, die meisten, wenn nicht alle in einem System gespeicherten Informationen erhalten kann, indem er bösartigen Code einfügt (z. B. Ich kann nicht glauben, dass ich vergessen habe, die Zeile zu entfernen, in der die Kreditkartendaten der Kunden vermerkt sind, sehr geehrter Herr! ).


Kleines Update (06.06.2012)

Gespeicherte Prozeduren (zumindest in Oracle) verhindern eine kontinuierliche Zustellung ohne Ausfallzeiten, da jede Änderung der Tabellenstruktur die Prozeduren und Trigger ungültig macht. Während der Aktualisierung der Datenbank ist die Anwendung daher ebenfalls inaktiv. Oracle bietet eine Lösung für diese Edition-Based Redefinition , aber die wenigen DBAs, die ich zu diesem Feature gefragt habe, erwähnten, dass es schlecht implementiert war und nicht in eine Produktions-DB aufgenommen werden würde.

Augusto
quelle
Nun, offensichtlich können Sie das tun, was Hibernate tut, und die Vererbung zum Generieren eines dynamischen Proxy-Objekts verwenden, mit dem Sie das Objektdiagramm abrufen können. Mit SP ist das allerdings extrem verrückt: D
Max
Also würde ich die Hälfte des Ruhezustands wieder herstellen, ohne die über 10-jährige Erfahrung des Ruhezustands-Teams :).
Augusto
1
Jeder DBA sollte das Löschen bestimmter Tabellen durch bestimmte Benutzer verhindern. Es sollte keine Rolle spielen, wie Sie es versuchen.
JeffO
1
Vielleicht werfen Sie einen Blick auf Mybatis - es bietet möglicherweise die Funktion, die Sie benötigen. Es ist weniger ein ORM als ein Mapping-Framework. Sie können SQL schreiben, wie Sie möchten, und Mybatis mitteilen, wo es in Ihrem Objektmodell abgelegt werden soll. Es werden große Objektdiagramme mit mehreren Abfragen verarbeitet, was sich nach der Situation anhört, in der Sie sich befinden (viele Thin Stored Procedures).
Michael K
1
@ Augusto: Ich war in einer ähnlichen Situation, nicht aufgrund der Verwendung von SPs, sondern aufgrund der Verwendung eines proprietären Mapping-Frameworks, das keine Objektbeziehungen unterstützt. Wir haben Tage damit verbracht, Code zu schreiben, der mit einem richtigen ORM in wenigen Minuten geschrieben werden konnte. Ich habe dieses Problem nie gelöst bekommen.
Kevin Cline

Antworten:

16

Ihre Anwendung sollte weiterhin nach domänenbasierten Entwurfsprinzipien modelliert sein. Es sollte keine Rolle spielen, ob Sie ein ORM, eine direkte JDBC, aufrufende SPs (oder was auch immer) verwenden . Hoffentlich sollte in diesem Fall eine dünne Schicht, die Ihr Modell von den SPs abstrahiert, den Trick machen. Wie in einem anderen Poster angegeben , sollten Sie die SPs und ihre Ergebnisse als Service anzeigen und die Ergebnisse Ihrem Domain-Modell zuordnen.

Martijn Verburg
quelle
Martijn, ich bin damit einverstanden, dass die App nach DDD-Prinzipien modelliert werden sollte, aber das Problem, dem ich gegenüberstehe (und bitte sagen Sie mir, ob es eine Lösung gibt !!), ist, dass einige gespeicherte Prozesse nur sehr wenige Informationen zurückgeben, um eine DDD-Entität zu instantiieren. In diesem Kommentar habe ich etwas mehr über die Informationen erklärt, die die gespeicherten Prozeduren zurückgeben. Ich könnte dies umgehen, indem ich mehr als eine gespeicherte Prozedur aufrufe und zum Beispiel alle Benutzerdetails abrufe und dann eine andere aufrufe, um alle Kontoinformationen abzurufen, aber es fühlt sich falsch an :).
Augusto
1
@Augusto Nun ... Sie sind App-Entwickler. Sie müssen sich also entscheiden, ob es sinnvoll ist, dass ein bestimmtes Objekt mit bestimmten auf NULL gesetzten Feldern existiert. Wenn es Sinn macht (zum Beispiel für eine bestimmte Aufgabe), dann lass es sein. Wenn nicht, bitten Sie den Autor von SP, weitere Daten bereitzustellen, damit Sie Ihre Objekte erstellen können.
Jacek Prucia
Und zu Jaceks Kommentar: Es ist durchaus akzeptabel, über 2 gespeicherte Prozesse aufzurufen. Stellen Sie sich diese wiederum als zwei Remote-Services vor, die Sie aufrufen müssen, um Ihr Domain-Modell zu erstellen.
Martijn Verburg
@Martijn: Nach meiner Erfahrung reicht eine dünne Schicht nicht aus. Der Zuordnungscode kann erheblich länger sein als die zugrunde liegende Geschäftslogik.
Kevin Cline
@ Kevin Cline - Guter Punkt, haben "hoffentlich" in der Antwort :-)
Martijn Verburg
5

Gibt es einen tatsächlichen Vorteil bei der Verwendung gespeicherter Prozeduren?

In der Finanzwelt (und an Orten, an denen die Einhaltung von Sarbanes-Oxley erforderlich ist) müssen Sie in der Lage sein, Systeme zu überwachen, um sicherzustellen, dass sie das tun, was sie tun sollen. In diesen Fällen ist es viel einfacher, die Konformität sicherzustellen, wenn der gesamte Datenzugriff über gespeicherte Prozeduren erfolgt. Und wenn alles Ad-hoc-SQL entfernt wird, ist es viel schwieriger, Dinge zu verbergen. Als Beispiel, warum dies eine "gute Sache" wäre, verweise ich Sie auf Ken Thompsons klassisches Papier Reflections on Trusting Trust .

Tangurena
quelle
ja millionenfach ja! Sie müssen auch sicherstellen, dass Benutzer nichts tun können, was sie nicht tun sollen, einschließlich der Tatsache, dass sie keine direkten Rechte für Tabellen und gespeicherte Prozesse haben.
HLGEM
1
Ich arbeite für eine Aktiengesellschaft und wir sind SOX-sompliant. Es mag an meinen schlechten Auditing-Kenntnissen liegen, aber ich sehe keinen Unterschied zwischen der Durchführung des Audits auf DB-Ebene (über gespeicherte Prozesse) oder auf Anwendungsebene. Jede Anwendung sollte ein eigenes DB-Schema haben, und auf dieses Schema kann nur von der Anwendung aus zugegriffen werden, anstatt dass es von verschiedenen Anwendungen gemeinsam genutzt wird.
Augusto
kaputte Verbindung ...
Alex R
@AlexR, feste Verbindung
Tangurena
2

Gespeicherte Prozeduren sind sehr viel effizienter als clientseitiger SQL-Code. Sie kompilieren SQL in der DB vor, wodurch sie auch einige Optimierungen durchführen können.

In der Architektur gibt ein SP die minimalen Daten zurück, die für eine Aufgabe erforderlich sind. Dies bedeutet, dass weniger Daten übertragen werden. Wenn Sie über eine solche Architektur verfügen, müssen Sie sich die DB als einen Service vorstellen (stellen Sie sich einen Web-Service vor und jeder SP ist eine Methode zum Aufrufen). Es sollte kein Problem sein, mit diesen Daten zu arbeiten, während ein ORM Sie dazu anleitet, mit Remote-Daten wie mit lokalen Daten zu arbeiten, und Sie so dazu verleitet, Leistungsprobleme einzuführen, wenn Sie nicht aufpassen.

Ich war in Situationen, in denen wir SPs vollständig verwendet haben, die DB eine Daten-API bereitgestellt und wir haben sie verwendet. Diese spezielle App war sehr umfangreich und lief erstaunlich gut. Ich werde nichts schlechtes über SPs danach gesagt haben!

Ein weiterer Vorteil ist, dass die DBAs alle Ihre SQL-Abfragen für Sie schreiben und die gesamte relationale Hierarchie in der DB problemlos verarbeiten, sodass Sie dies nicht tun müssen.

gbjbaanb
quelle
3
gbjbaanb, das meiste, was Sie gesagt haben, gilt für ältere Datenbanken. In den meisten neueren Datenbanken werden Abfragen häufig neu kompiliert, um zu entscheiden, welche neuen Optimierungen verwendet werden sollen (selbst wenn sie in gespeicherter, produzierter Form vorliegen). Ich stimme dem zu, was Sie über die Verwendung der Datenbank als externes System gesagt haben, aber ich sehe auch, dass dies eine Menge Arbeit bedeutet, da die Anwendung die Datenbank besitzt und beide so weit wie möglich synchron sein sollten. Zum Beispiel bei der Benennung von Tabellen / Klassen und Feldern / Spalten. Der Ansatz, die DBAs die Prozeduren schreiben zu lassen, riecht eher nach Entwicklungssilos als nach einem multidisziplinären Team.
Augusto
7
SPs sind nicht immer effizienter und ich denke, die Weitergabe von SQL an DBAs ist ein schlechter Weg. Als Domain-Experte sollte der Entwickler wissen, welche Daten er erhalten möchte und wie er sie erhält
Martijn Verburg
1
Dies ist eine gute Antwort, aber meiner Erfahrung nach benötigen die meisten Kunden nicht die Leistungssteigerung, um den Datenzugriff über gespeicherte Prozeduren zu steuern. In den meisten Fällen werden diese Architekturentscheidungen in Softwareläden getroffen, in denen die aufgeblähten Gehälter von Programmierern mit "grauem Bart" für gespeicherte Prozeduren gerechtfertigt werden müssen, die über keine anderen Fähigkeiten verfügen.
maple_shaft
1
@Augusto the approach of letting the DBAs write the procedures smells like development silos+100 Internet für dieses Juwel der Wahrheit. Ich habe dies immer als den Fall angesehen, in dem der Datenzugriff über gespeicherte Prozeduren gesteuert wurde.
maple_shaft
1
@maple_shaft: Warum werden die Datenbankadministratoren, die SPs schreiben, nicht als Teil des Entwicklerteams betrachtet? Wo es funktioniert, sind sie spezialisierte Programmierer, die diesen Aspekt des Systems sehr gut kennen, viel besser als die meisten Universalentwickler. Dies könnte das Problem sein, das zur Popularität von ORMs geführt hat. Ich meine, niemand würde zweimal darüber nachdenken, ob ein Designer die GUI übernimmt. Warum also den Hass für einen Datenarchitekten, der das Schema erstellt?
gbjbaanb
2

Was häufig passiert, ist, dass Entwickler ihre ORM-Objekte fälschlicherweise als Domänenmodelle verwenden.

Dies ist falsch und bindet Ihre Domain direkt an Ihr DB-Schema.

Was wirklich haben sollte, sind separate Domain-Modelle, die so umfangreich sind, wie Sie möchten, und die ORM-Ebene wird separat verwendet.

Dies bedeutet, dass Sie eine Zuordnung zwischen jedem Satz von Objekten benötigen.

ozz
quelle
1
Dies ist eine gute Idee, aber bei kleineren Projekten fühlt es sich wirklich nach Overkill an. Dieser Ansatz erfordert auch eine Übersetzungsschicht zwischen der ORM-Persistenzschicht und dem Domänenmodell.
maple_shaft
@maple_shaft stimmte zu, und das habe ich mit "Mapping" gemeint :-)
ozz
@Ozz, die Art und Weise, wie ich gearbeitet habe, ist genau die, die Entitätsklassen sind das Domain-Modell (und ich könnte mit ziemlich viel Erfolg hinzufügen). Ich bin damit einverstanden, dass das Domänenmodell an das Schema gebunden wird, aber genau das möchte ich, da ich Konventionen anstelle von Konfigurationen verwende. Der nette Nebeneffekt ist, dass ich mir keine Gedanken machen muss, wenn ich ein Feld in einer Entität sehe über den Namen der Tabelle und der Spalte, in der diese Informationen gespeichert sind.
Augusto
@ Augusto Ich habe es auch getan! und wie maple_shaft sagt, ist es in Ordnung für kleine CRUD-Apps, aber es gibt viele Probleme, wie das OP herausfindet. Ein Beispiel könnte sein, dass Sie eine viele-zu-viele-Zuordnungstabelle haben, z. B .: StudentClasses, die Students Klassen zuordnet und nur StudentID und classID enthält. Sie möchten dies nicht unbedingt in Ihrer Domain zuordnen. Das ist nur ein gutes Beispiel.
ozz
2
@Ozz: Ihr Kommentar scheint der Idee eines ORM zu widersprechen. Ein ORM bindet Ihre Domain nicht direkt an Ihr DB-Schema. Der ORM ordnet Ihre Domäne einem DB-Schema zu, ohne dass eine separate DAO-Schicht erforderlich ist. Das ist der springende Punkt eines ORM. Und die meisten ORMs verarbeiten viele-zu-viele-Zuordnungen einwandfrei, ohne dass ein Domänenmodell für die Zuordnungstabelle erforderlich ist.
Kevin Cline
1

Ihre Domain-Objekte können nach Belieben ausgefüllt werden. Die Verwendung von Hibernate ist nicht erforderlich. Ich denke, der richtige Begriff ist Data-Mapper . Es ist sehr wahrscheinlich, dass Ihre persistierten Daten eine völlig andere Struktur aufweisen als Ihre Domain-Objekte.

NimChimpsky
quelle
Wir verwenden derzeit Data Mappers, aber das Problem ist, dass die gespeicherten Procs einen minimalen Datensatz zurückgeben, der manchmal nicht ausreicht, um ein Objekt zu füllen (möglicherweise sollten wir zulassen, dass die gespeicherten Prozeduren weitere Informationen zurückgeben). Beispielsweise kann ein Geschäftsprozess eine Benutzer-E-Mail, einen Vor- und Nachnamen zurückgeben. während eine andere Benutzer-ID und eine Adresse hinzufügt. Da die Daten unterschiedlich sind, verwenden wir unterschiedliche Objekte zum Speichern der Daten, was bedeutet, dass wir unterschiedliche 'User'-Klassen haben. Ich versuche, Vererbung hier zu vermeiden, weil ich denke, dass es eine falsche Verwendung ist.
Augusto
@ Augusto: Schnittstellen?
Kramii Reinstate Monica
@Karmii, ich glaube nicht, dass die Schnittstellen hier helfen, da wir dann die Logik in verschiedenen Klassen duplizieren müssten. Oder wir könnten Schnittstellen verwenden und dann die Verarbeitung an eine Hilfsklasse delegieren, aber das ist nicht wirklich OO :(.
Augusto
1
@Augusto ich das Problem nicht verstehen: „gespeicherte Procs gibt einen minimalen Satz von Daten, die manchmal nicht genug ist , um ein Objekt zu füllen“ So ermitteln Sie den sproc ändern oder einen anderen erstellen und dann die Daten Mapper die Zuordnung zu tun lassen
NimChimpsky