DDD: Identität der Entität, bevor sie beibehalten wird

8

In der Domain Driven Design ist eines der definierenden Merkmale einer Entity, dass sie eine Identität hat.

Problem:

Ich kann Entitäten bei der Instanzerstellung keine eindeutige Identität zuweisen. Diese Identität wird nur vom Repository bereitgestellt, sobald die Entität beibehalten wurde (dieser Wert wird von der zugrunde liegenden Datenbank bereitgestellt).

Ich kann an dieser Stelle nicht Guid Werte verwenden. Die vorhandenen Daten werden mit int Primärschlüsselwerten gespeichert und ich kann kein eindeutiges int bei Instanziierung generieren.

Meine Lösung:

  • Jede Entität hat einen Identitätswert
  • Die Identität wird nur dann auf eine echte Identität gesetzt, wenn sie dauerhaft ist (von der Datenbank bereitgestellt)
  • Die Identität wird auf Standard gesetzt, wenn sie vor der Persistenz instanziiert wurde
  • Wenn die Identität Standard ist, sind die Entitäten durch den Verweis
  • vergleichbar
  • Wenn die Identität nicht standardmäßig ist, sind Entitäten durch Identitätswerte
  • vergleichbar

Code (die abstrakte Basisklasse für alle Entitäten):

%Vor%

Frage:

  • Würdest du dies als eine gute Alternative zum Generieren von Guid-Werten bei der Instanzerstellung betrachten?
  • Gibt es bessere Lösungen für dieses Problem?
davenewza 21.01.2014, 06:22
quelle

5 Antworten

4
  

Ich kann Entitäten bei der Instanzerstellung keine eindeutige Identität zuweisen. Diese Identität wird nur vom Repository bereitgestellt, sobald die Entität beibehalten wurde (dieser Wert wird von der zugrunde liegenden Datenbank bereitgestellt).

Wie viele Orte haben Sie, wo Sie eine Liste von Entitäten desselben Typs erstellen und mehr als eine Entität mit der Standard-ID haben?

  

Würdest du dies als eine gute Alternative zur Erzeugung von Guid-Werten bei der Instanzerstellung betrachten?

Wenn Sie kein ORM verwenden, ist Ihre Vorgehensweise gut genug. Insbesondere, wenn eine Implementierung der Identitätskarte und Arbeitseinheit ist Ihre Verantwortlichkeit. Aber du hast nur Equals(object obj) festgelegt. GetHashCode() -Methode überprüft nicht, ob uniqueId.Equals(default(IdType)) .

Ich schlage vor, dass Sie in jede Open-Source- "Infrastructure Boilerplate" wie Sharp-Architecture schauen und sie überprüfen Implementierung der Basisklasse für alle Domain-Entitäten .

Ich bin es gewohnt, benutzerdefinierte Implementierungen von Equals() für Domain-Entities zu schreiben, aber es kann überflüssig sein, wenn es um die Verwendung von ORM geht. Wenn Sie ein ORM verwenden, werden Implementierungen der Identitätskarte und Einheit der Arbeit Muster aus der Box und Sie können sich auf sie verlassen.

    
Ilya Palkin 21.01.2014, 22:32
quelle
5

Sie können einen Sequenzgenerator verwenden, um eindeutige int / long Identifikatoren zu generieren, wenn Sie ein Entitätsobjekt instanziieren.

Die Schnittstelle sieht wie folgt aus:

%Vor%

Eine typische Implementierung eines Sequenzgenerators verwendet eine Sequenztabelle in der Datenbank. Die Sequenztabelle enthält zwei Spalten: sequenceName und allocatedSequence .

Wenn getNextSequence zum ersten Mal aufgerufen wird, schreibt es einen großen Wert (zB 100 ) in die Spalte allocatedSequence und gibt 1 zurück. Der nächste Aufruf gibt 2 zurück, ohne auf die Datenbank zugreifen zu müssen. Wenn die 100 Sequenzen ablaufen, liest und inkrementiert es die allocatedSequence by 100 erneut.

Sehen Sie sich den SequenceHiLoGenerator im Hibernate-Quellcode an. Es macht grundsätzlich das, was ich oben beschrieben habe.

    
nwang0 21.01.2014 19:18
quelle
2

Ich glaube, die Lösung dafür ist eigentlich ziemlich einfach:

  • Wie Sie erwähnen, müssen Entitäten eine Identität haben,

  • Gemäß Ihren (vollkommen gültigen) Anforderungen wird die Identität Ihrer Entitäten zentral vom DBMS zugewiesen,

  • Daher ist jedes Objekt, dem noch keine Identität zugewiesen wurde, nicht eine Entität.

Was Sie hier behandeln, ist eine Art von Data Transfer Object-Typ, der keine Identität hat. Sie sollten darüber nachdenken, wie Sie Daten von jedem Eingabesystem, das Sie verwenden, über das Repository an das Domänenmodell übertragen (das Sie als Schnittstelle für die Identitätszuweisung benötigen). Ich schlage vor, dass Sie einen anderen Typ für diese Objekte (eines ohne Schlüssel) erstellen und an die Methode "Hinzufügen / Erstellen / Einfügen / Neu" Ihres Repositorys übergeben.

Wenn die Daten nicht viel Vorverarbeitung benötigen (d. h. nicht viel herumgereicht werden müssen), lassen manche Leute sogar DTOs weg und leiten die verschiedenen Daten über die Methodenargumente direkt weiter. So sollten Sie solche DTOs betrachten: als bequeme Argumentobjekte. Beachten Sie erneut das Fehlen eines "Schlüssel" - oder "ID" -Arguments.

Wenn Sie das Objekt als Entity bearbeiten müssen, bevor Sie es in die Datenbank einfügen, sind DBMS-Sequenzen die einzige Option. Beachten Sie, dass dies in der Regel relativ selten ist. Der einzige Grund, warum Sie dies tun müssen, ist, ob die Ergebnisse dieser Manipulationen den Objektstatus ändern, sodass Sie eine zweite Anforderung zum Aktualisieren in der Datenbank stellen müssen würde sicherlich lieber vermeiden.

Sehr oft sind "Erstellungs-" und "Modifikations" -Funktionalitäten in Anwendungen so eindeutig, dass Sie immer zuerst die Datensätze für die Entitäten in der Datenbank hinzufügen, bevor Sie sie später erneut abrufen, um sie zu ändern.

Sie werden sich zweifellos Sorgen über die Wiederverwendung von Code machen. Je nachdem, wie Sie Ihre Objekte konstruieren, möchten Sie wahrscheinlich eine Validierungslogik ausschließen, damit das Repository die Daten überprüfen kann, bevor Sie sie in die Datenbank einfügen. Beachten Sie, dass dies normalerweise unnötig ist, wenn Sie DBMS-Sequenzen verwenden, und dies könnte ein Grund dafür sein, dass manche Leute sie systematisch verwenden, selbst wenn sie sie nicht unbedingt benötigen. Abhängig von Ihren Leistungsanforderungen sollten Sie die obigen Kommentare berücksichtigen, da eine Sequenz einen zusätzlichen Rundlauf generiert, den Sie oft vermeiden können.

  • Beispiel: Erstellen Sie ein Validatorobjekt, das Sie sowohl in der Entität als auch im Repository verwenden.

Disclaimer: Ich habe keine gründliche Kenntnis der kanonischen DDD, ich würde nicht wissen, ob das wirklich der empfohlene Ansatz ist, aber es macht Sinn für mich.

Ich füge auch hinzu, dass das Ändern des Verhaltens von Equals (und anderer Methoden) auf der Grundlage dessen, ob das Objekt eine Entität oder ein einfaches Datenobjekt darstellt, meiner Meinung nach nicht ideal ist. Bei der von Ihnen verwendeten Technik müssen Sie außerdem sicherstellen, dass der Standardwert, den Sie für den Schlüssel verwenden, ordnungsgemäß aus der Wertdomäne in der gesamten Domänenlogik ausgeschlossen wird.

Wenn Sie immer noch diese Technik verwenden möchten, empfehle ich, einen dedizierten Typ für den Schlüssel zu verwenden. Bei diesem Typ wird der Schlüssel mit einem zusätzlichen Status versehen bzw. umschlossen, der angibt, ob der Schlüssel vorhanden ist oder nicht. Beachten Sie, dass diese Definition Nullable<T> so sehr ähnelt, dass ich sie in Erwägung ziehen würde (Sie können die Syntax type? in C # verwenden). Mit diesem Design ist es klarer, dass Sie dem Objekt erlauben, keine Identität zu haben (Null-Schlüssel). Es sollte auch offensichtlicher sein, warum das Design nicht ideal ist (wieder, meiner Meinung nach): Sie verwenden denselben Typ, um sowohl Entitäten als auch identitätslose Datenübertragungsobjekte darzustellen.

    
tne 18.08.2014 13:47
quelle
1
  

Ich kann an dieser Stelle nicht anfangen, Guid-Werte zu verwenden.

Ja, Sie können und das wäre eine Alternative. Guids wären nicht die primären Schlüssel Ihrer Datenbank, sondern würden auf der Ebene des Domänenmodells verwendet. Bei diesem Ansatz könnten Sie sogar zwei separate Modelle haben - ein Persistenzmodell mit Ints als Primärschlüssel und -linien als Attribute und ein anderes Modell, das Domänenmodell, in dem die Guids die Rolle von Identifikatoren spielen.

Auf diese Weise können Ihre Domänenobjekte ihre Identitäten erhalten, sobald sie erstellt wurden, und Persistenz ist nur eine der kleineren geschäftlichen Angelegenheiten.

Die andere mir bekannte Option ist die, die Sie beschrieben haben.

    
Wiktor Zychla 21.01.2014 07:16
quelle
1

Ihre vorgeschlagene Lösung ist meiner Erfahrung nach vollkommen richtig. Ich habe diesen Ansatz ziemlich oft benutzt.

Wenn Sie etwas noch besser wollen, hier ist ein weiterer Vorschlag. Was wäre, wenn Sie einen Wert haben könnten, der ( für unsere Absichten und Zwecke ) so gut wie eine GUID ist, aber in eine long passt? Was wäre, wenn es auch ohne ein Repository reaktiv wäre?

Mir ist klar, dass Ihre Tabelle momentan nur ein int als PRIMARY KEY enthält. Aber wenn Sie dies in long oder für Ihre zukünftigen Tabellen ändern können, könnte mein Vorschlag für Sie von Interesse sein.

In Vorschlag: lokal eindeutige GUID-Alternative erkläre ich, wie man eine lokal einzigartige, neue, streng aufsteigende 64- Bit-Wert. Es ersetzt die Kombination aus Auto-Inkrement-ID und GUID.

Ich habe immer die Idee gemocht, sowohl eine numerische ID als auch eine GUID zu haben. Es ist wie zu sagen: "Dies ist die eindeutige Kennung der Entität. Und ... das ist ihre andere eindeutige Kennung." Sicher, Sie können einen Bereich außerhalb der Domain und der Sprache beibehalten, aber Sie haben damit das technische Problem, gleichzeitig und zu verwalten und die zusätzliche numerische ID zu verbergen. Wenn Sie eine einzelne ID haben möchten, die sowohl domänenfreundlich ist (mit einem Repository umbenannt werden kann, als auch eine benannte ID anstelle von GUID) und datenbankfreundlich (klein, schnell und aufsteigend), versuchen Sie meinen Vorschlag.

Ich warne Sie, dass die Implementierung schwierig sein kann, insbesondere in Bezug auf Kollisionen und Thread-Sicherheit. Ich habe noch keinen Code dafür geschrieben.

    
Timo 29.10.2017 13:29
quelle