Nach ein paar nützlichen Antworten, ob ich hier ein Domänenobjekt oder eine eindeutige ID als Methoden- / Funktionsparameter verwenden soll. Kennung vs. Domänenobjekt als Methodenparameter , habe ich eine ähnliche Frage zu: Mitgliedern (die vorherige Fragendiskussion hat es nicht geschafft decken Sie dies ab). Was sind die Vor- und Nachteile der Verwendung eindeutiger IDs als Mitglied gegenüber Objekt als Mitglied? Ich frage in Bezug auf stark typisierte Sprachen wie Scala / C # / Java. Sollte ich haben (1)
User( id: Int, CurrentlyReadingBooksId: List[Int])
Book( id: Int, LoanedToId: Int )
oder (2), bevorzugt gegenüber (1) Nach dem Durchlaufen: Sollen wir Typen für alles definieren?
User( id: UserId, CurrentlyReadingBooksId: List[ BookId] )
Book( id: BookId, LoanedToId: UserId )
oder (3)
User( id: Int, CurrentlyReadingBooks: List[Book])
Book( id: Int, LoanedTo: User)
Während ich mir keine Vorteile für das Objekt (3) vorstellen kann, besteht ein Vorteil für die IDs (2) und (1) darin, dass ich beim Erstellen des Benutzerobjekts aus der Datenbank nicht das Buchobjekt erstellen muss, das kann wiederum vom Benutzerobjekt selbst abhängen und eine endlose Kette bilden. Gibt es eine generische Lösung für dieses Problem sowohl für RDBMS als auch für No-SQL (wenn sie unterschiedlich sind)?
Basierend auf einigen Antworten, die meine Frage bisher umformuliert haben: (unter Verwendung von IDs, die in verpackten Typen vorliegen sollen) 1) Immer IDs verwenden? 2) Immer Objekte verwenden? 3) Verwenden Sie IDs, wenn beim Serialisieren und Deserialisieren die Gefahr einer Rekursion besteht, aber verwenden Sie Objekte anderweitig? 4) Sonst noch etwas?
BEARBEITEN: Wenn Sie antworten, dass Objekte immer oder in einigen Fällen verwendet werden sollen, stellen Sie bitte sicher, dass Sie die größte Sorge beantworten, die andere Antwortende gestellt haben => So erhalten Sie Daten aus der Datenbank
quelle
Antworten:
Domänenobjekte als IDs verursachen einige komplexe / subtile Probleme:
Serialisierung / Deserialisierung
Wenn Sie Objekte als Schlüssel speichern, wird die Serialisierung des Objektdiagramms äußerst kompliziert. Sie erhalten
stackoverflow
Fehler , wenn aufgrund der Rekursion eine naive Serialisierung JSON oder XML zu tun. Sie müssen dann einen benutzerdefinierten Serializer schreiben, der die tatsächlichen Objekte konvertiert, um ihre IDs zu verwenden, anstatt die Objektinstanz zu serialisieren und die Rekursion zu erstellen.Übergeben Sie Objekte zur Typensicherheit, speichern Sie jedoch nur IDs. Anschließend können Sie eine Zugriffsmethode verwenden, die die zugehörige Entität beim Aufruf verzögert lädt. Das Caching der zweiten Ebene kümmert sich um nachfolgende Anrufe.
Subtile Referenzlecks:
Wenn Sie Domänenobjekte in Konstruktoren verwenden, wie Sie sie dort haben, erstellen Sie Zirkelverweise, bei denen es sehr schwierig ist, Speicher für Objekte zurückzugewinnen, die nicht aktiv verwendet werden.
Ideale Situation:
Undurchsichtige IDs vs int / long:
Ein
id
sollte ein vollständig undurchsichtiger Bezeichner sein, der keine Informationen darüber enthält, was er identifiziert. Es sollte jedoch eine gewisse Bestätigung bieten, dass es sich um eine gültige Kennung in seinem System handelt.Rohe Typen brechen dies:
int
,long
UndString
sind die am häufigsten rohen Typen für Bezeichner in RDBMS - System verwendet. Es gibt eine lange Geschichte praktischer Gründe, die Jahrzehnte zurückreichen, und alle sind Kompromisse, die entweder zum Sparenspace
oder zum Sparentime
oder zu beidem passen .Sequentielle IDs sind die schlimmsten Straftäter:
Wenn Sie eine sequentielle ID verwenden, packen Sie standardmäßig zeitliche semantische Informationen in die ID. Welches ist nicht schlecht, bis es verwendet wird. Wenn Leute anfangen, Geschäftslogik zu schreiben, die die semantische Qualität der ID sortiert oder filtert, dann schaffen sie eine Welt voller Schmerzen für zukünftige Betreuer.
String
Felder sind problematisch, weil naive Designer Informationen in den Inhalt packen, normalerweise auch zeitliche Semantik.Diese machen es unmöglich, auch ein verteiltes Datensystem zu erstellen, da
12437379123
es global nicht eindeutig ist. Die Wahrscheinlichkeit, dass ein anderer Knoten in einem verteilten System einen Datensatz mit derselben Nummer erstellt, ist so gut wie garantiert, wenn Sie genügend Daten in einem System erhalten.Dann beginnen Hacks, es zu umgehen, und das Ganze verwandelt sich in einen Haufen dampfenden Chaos.
Wenn Sie große verteilte Systeme ( Cluster ) ignorieren , wird dies zu einem Albtraum, wenn Sie versuchen, die Daten auch mit anderen Systemen zu teilen. Besonders wenn das andere System nicht unter Ihrer Kontrolle ist.
Sie haben genau das gleiche Problem, wie Sie Ihre ID global eindeutig machen können.
UUID wurde aus einem Grund erstellt und standardisiert:
UUID
kann unter allen oben aufgeführten Problemen leiden, je nachdem, welcheVersion
Sie verwenden.Version 1
Verwendet eine MAC-Adresse und Zeit, um eine eindeutige ID zu erstellen. Dies ist schlecht, da es semantische Informationen über Ort und Zeit enthält. Das ist an sich kein Problem, wenn naive Entwickler sich für die Geschäftslogik auf diese Informationen verlassen. Dadurch werden auch Informationen verloren, die bei Eindringversuchen ausgenutzt werden könnten.Version 2
Die Verwendung eines BenutzersUID
oder einesGID
DomiansUID
oderGUI
anstelle der ZeitVersion 1
davon ist genauso schlimm wieVersion 1
für Datenlecks und das Risiko, dass diese Informationen in der Geschäftslogik verwendet werden.Version 3
ist ähnlich, ersetzt jedoch die MAC-Adresse und die Zeit durch einenMD5
Hash eines Arraysbyte[]
von etwas, das definitiv eine semantische Bedeutung hat. Es gibt keinen Datenverlust, über den Siebyte[]
sich Sorgen machen müssen. Der Datenverlust kann nicht behoben werdenUUID
. Dies gibt Ihnen eine gute Möglichkeit,UUID
Instanzformulare und externe Schlüssel deterministisch zu erstellen .Version 4
basiert nur auf Zufallszahlen, was eine gute Lösung ist, es enthält absolut keine semantischen Informationen, aber es ist nicht deterministisch wiederherstellbar.Version 5
ist genauso wieVersion 4
aber verwendetsha1
stattmd5
.Domänenschlüssel und Transaktionsdatenschlüssel
Ich bevorzuge Domänenobjekt-IDs, wenn ich sie aus technischen Gründen verwende
Version 5
oderVersion 3
wenn ich sie nicht verwenden darfVersion 5
.Version 3
eignet sich hervorragend für Transaktionsdaten, die möglicherweise auf viele Computer verteilt sind.Verwenden Sie eine UUID, es sei denn, Sie sind durch Speicherplatz eingeschränkt:
Sie sind garantiert eindeutig, geben Daten aus einer Datenbank aus und laden sie erneut in eine andere. Sie mussten sich nie um doppelte IDs sorgen, die tatsächlich auf unterschiedliche Domänendaten verweisen.
Version 3,4,5
sind völlig undurchsichtig und so sollten sie sein.Sie können eine einzelne Spalte als Primärschlüssel mit einem
UUID
und dann zusammengesetzte eindeutige Indizes für einen natürlichen zusammengesetzten Primärschlüssel haben.Speicher muss auch nicht sein
CHAR(36)
. Sie können dasUUID
Feld in einem nativen Byte- / Bit- / Zahlenfeld für eine bestimmte Datenbank speichern, solange es noch indizierbar ist.Erbe
Wenn Sie unformatierte Typen haben und diese nicht ändern können, können Sie sie dennoch in Ihrem Code abstrahieren.
Wenn Sie einen
Version 3/5
vonUUID
Ihnen verwenden, können Sie dasClass.getName()
+String.valueOf(int)
als a übergebenbyte[]
und einen undurchsichtigen Referenzschlüssel haben, der neu erstellt und deterministisch ist.quelle
C-> A -> B -> A
undB
in einCollection
Dann gesetzt wirdA
und alle seine Kinder noch erreichbar sind, sind diese Dinge nicht ganz offensichtlich und können zu subtilen Lecks führen .GC
ist das geringste der Probleme, Serialisierung und Deserialisierung des Graphen ist ein Albtraum der Komplexität.Ja, beide Wege haben Vorteile, und es gibt auch einen Kompromiss.
List<int>
::User
Users
TabelleList<Book>
::Wenn Sie keine Speicher- oder CPU-Bedenken haben, würde
List<Book>
der Code, der dieUser
Instanzen verwendet, sauberer sein.Kompromiss:
Bei Verwendung von Linq2SQL wird für den für die Entität Benutzer generierten Code ein Code
EntitySet<Book>
geladen, der beim Zugriff verzögert geladen wird. Dies sollte Ihren Code sauber halten und die Benutzerinstanz klein halten (Speicherbedarf).quelle
Kurze und einfache Faustregel:
IDs werden in DTOs verwendet .
Objektreferenzen werden normalerweise in den Layerobjekten Domain Logic / Business Logic und UI verwendet.
Das ist die übliche Architektur in größeren, unternehmerisch genug Projekten. Sie haben Mapper, die diese beiden Arten von Objekten hin und her übersetzen.
quelle
BookRepository
und ein habenUserRepository
. Sie rufen immermyRepository.GetById(...)
oder ähnliches auf, und das Repository erstellt entweder das Objekt und lädt seine Werte aus einem Datenspeicher oder ruft es aus einem Cache ab. Außerdem werden untergeordnete Objekte meist faul geladen, wodurch verhindert wird, dass während der Erstellungszeit direkte Zirkelverweise verarbeitet werden müssen.