Einführung
- Lesen Sie das DDD-Buch und hinterlassen Sie eine Notiz über Ihr Verständnis (von Zeit zu Zeit aktualisiert).
――Um die Überprüfbarkeit zu gewährleisten, versuche ich, meine Meinung so weit wie möglich zu zitieren.
――Ich denke, dass das Implementierungsbeispiel neben dem DDD-Konzept aus den Java-Sprachspezifikationen bestimmt wird.
- Das Codebeispiel setzt eine Todo-App wie Wunderlist voraus.
Verschiedene Gefühle auf dem Weg
――Wenn Sie die Erläuterungen zum Domain-Modell und zum Domain-Service lesen, bin ich der festen Überzeugung, dass die Kenntnis der Geschäftsregeln zu einer Problemisolierung führen wird.
- Zentrale Verarbeitung (Domänenmodell, Repository, Factory) als Konzept, das eine Einrichtung namens Bank haben sollte
――Sie können nicht mehr als Ihren Kontostand abheben
――Was ist der Zinssatz für Anlageprodukte?
- Geschäft des Bankangestellten (Domain-Service)
――Übertragungsverarbeitung, Zusammenbauverarbeitung usw.
- Anwendungsservice zur Steigerung der Produktivität von Bankangestellten
――Auswechseln in Yen oder Japanisch, Abrufen von Formularen in XML usw.
- Infra Service des allgemeinen Systems
- Sie werden per E-Mail benachrichtigt, dass die Übertragung abgeschlossen wurde.
Hmmm, ich habe das Gefühl, dass die Todo-App ein schlechtes Beispiel hat. Im Fall von Todo bleibt keine andere Wahl, als es einzugeben oder zu löschen, und es gibt fast keine Berechnung. Die einzige Berechnung, die ein Sammlungsobjekt haben sollte, besteht darin, DueDate in aufsteigender Reihenfolge zu sortieren, Favoriten (true) zu sammeln und sie vorne und den Rest hinten auszurichten. Sie können die Verdaulichkeit von Task berechnen. Es gibt keine Verantwortung, etwas mit einem Todo zu berechnen.
layer
+-- domain
| |
| +-- model
| | |
| | +-- Entity -- Aggregate
| | |
| | `-- Value Object
| |
| +-- Factory
| |
| +-- Repository ( command , query )
| |
| +-- Service
| |
| `-- ( shared )
|
+-- infrastructure - service
| | |
| | `-- ( mail sender )
| |
| `-- ( Repository Implementation )
| |
| +-- ( db I/O )
| |
| +-- ( file I/O )
| |
| `-- ( cache I/O )
|
+-- application -- Service
|
`-- interfaces
|
+-- ( controller )
| |
| `-- ( REST )
| |
| +-- ( XML )
| |
| `-- ( JSON )
|
+-- ( view )
| |
| `-- ( HTML )
|
`-- ( socket )
domain model
Nachschlagewerke:
--Konzept / Erklärung
- [Eric Evans Domain Driven Design (Evans)](https://www.amazon.co.jp/%E3%82%A8%E3%83%AA%E3%83%83%E3%82%AF % E3% 83% BB% E3% 82% A8% E3% 83% B4% E3% 82% A1% E3% 83% B3% E3% 82% B9% E3% 81% AE% E3% 83% 89% E3 % 83% A1% E3% 82% A4% E3% 83% B3% E9% A7% 86% E5% 8B% 95% E8% A8% AD% E8% A8% 88-Architekten% E2% 80% 99 Archiv-% E3% 82% BD% E3% 83% 95% E3% 83% 88% E3% 82% A6% E3% 82% A7% E3% 82% A2% E9% 96% 8B% E7% 99% BA% E3% 81% AE% E5% AE% 9F% E8% B7% B5-% E3% 82% A8% E3% 83% AA% E3% 83% 83% E3% 82% AF% E3% 83% BB% E3% 82 % A8% E3% 83% B4% E3% 82% A1% E3% 83% B3% E3% 82% B9 / dp / 4789121967)
- [Praktisches domänengesteuertes Design (Vernon)](https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5%E3%83%89%E3%83%A1%E3% 82% A4% E3% 83% B3% E9% A7% 86% E5% 8B% 95% E8% A8% AD% E8% A8% 88-% E3% 83% B4% E3% 82% A1% E3% 83 % BC% E3% 83% B3% E3% 83% BB% E3% 83% B4% E3% 82% A1% E3% 83% BC% E3% 83% 8E% E3% 83% B3-ebook / dp / B00UX9VJGW )
- Implementierungsbeispiel
- Auf dem Gebiet nützliche Prinzipien des Systemdesigns (Masuda)
- Katojuns technisches Tagebuch DDD (Kato)
Entity
- Charakteristisch
- Hat eine unveränderliche Kennung während seines gesamten Lebenszyklus.
- Die Identität wird durch eine Kennung bestätigt.
- Andere Attribute als Bezeichner sind variabel.
- Im Prinzip erfolgen Attributoperationen über Geschäftslogik, nicht über Setter.
- Implementieren Sie equals () und hashCode () selbst
Entitätsübersicht
//Andere Variablenattribute als Bezeichner
//Weil die Attribute in dieser Klasse unabhängig voneinander sind@Setter ist angegeben
//Wenn Attribute miteinander verknüpft sind, führen Sie Attributoperationen über diese Klasse von Geschäftslogik aus.
@Getter
@Setter
public final class Todo extends AbstractEntity<Todo> implements ValueObject {
// (AbstractEntity::private final UUID identifier)
private Title title;
private DueDate dueDate;
private TaskList taskList;
private Memo memo;
private Favorite favorite;
public Todo(final EntityIdentifier<Todo> identifier, final Title title, final DueDate dueDate, final TaskList taskList, final Memo memo, final Favorite favorite) {
super(identifier);
Validate.notNull(title);
Validate.notNull(dueDate);
Validate.notNull(taskList);
Validate.notNull(memo);
Validate.notNull(favorite);
this.title = title;
this.dueDate = dueDate;
this.taskList = taskList;
this.memo = memo;
this.favorite = favorite;
}
@Override
public Todo clone() {
return super.clone();
}
@Override
public boolean equals(final Object that) {
if (this == that) {
return true;
}
if (that == null || getClass() != that.getClass()) {
return false;
}
Todo o = (Todo) that;
if(!entityIdentifier.equals(o.entityIdentifier)){
return false;
}
return true;
}
@Override
public int hashCode() {
int result = Objects.hashCode(entityIdentifier);
result = 31 * result;
return result;
}
}
- ** Ich möchte Objekte anhand der Identität verfolgen (= während des gesamten Lebenszyklus eine unveränderliche Kennung haben) **
--Was ist die Entitätsidentität?
- Unterscheidet sich von der Gleichheit, die der Java == -Operator vergleicht
-
- Dieser Identitätsmechanismus ist in anderen Anwendungsbereichen wenig sinnvoll. (Evans Nr. 2348-2349) *
- Es ist ein Mechanismus, um die Gleichheit im Gedächtnis zu erkennen, und wenn es aus dem Gedächtnis verschwindet, geht die Identität verloren.
-
- Identität ist ein cleveres und aussagekräftiges Attribut einer Entität und kann nicht von der automatisierten Funktionalität einer Programmiersprache geerbt werden. (Evans Nr. 2349-2350) *
-
- Kontinuität während des gesamten Lebenszyklus, wobei wichtige Unterscheidungen unabhängig von Attributen für Anwendungsbenutzer getroffen werden (Evans Nr. 2339-2340) *
- Identität = Äquivalenz basierend auf Identität, Äquivalenz = Wertgleichheit
-
- Es ist keine Entität, die normalerweise als Entität bezeichnet wird. (Evans Nr. 2339) *
- In einigen Fällen kann es sich um Entity oder ValueObject handeln
――Das Konzept der Wörter enthält kein Trennzeichen. Es hängt davon ab, wie sie im Modell behandelt werden
-
- Die Software des Versandhandelsunternehmens benötigt eine Adresse zur Bestätigung der Kreditkarte und zur Adressierung des Pakets. Selbst wenn die Mitbewohner bei demselben Unternehmen bestellen, ist es nicht wichtig zu wissen, dass beide am selben Ort leben. In diesem Fall ist die Adresse ein Wertobjekt. (Evans Nr. 2500-2502) *
-
- Postdienstsoftware hat ein hierarchisches Format, das aus Provinzen, Städten, Postbezirken und Bezirken besteht, um Zustellwege zu systematisieren, das sich auf einzelne Adressen erstreckt. Diese Adressobjekte leiten die Postleitzahl vom übergeordneten Element in der Hierarchie ab. Wenn der Postdienst beschließt, den Postbezirk neu zuzuweisen, werden alle darin enthaltenen Adressen zusammen verschoben. Hier ist die Adresse eine Entität. (Evans Nr. 2503-2506 *
- ** Die Identität wird garantiert, indem die Kennung unveränderlich gemacht wird. Daher werden die Attribute neu geschrieben **
-
- Wenn ein Objekt eher durch Identität als durch Attribut identifiziert wird, sollte diese Identität die erste Priorität bei der Definition dieses Objekts im Modell sein. (Evans Nr. 2363-2364) *
Value Object
- Charakteristisch
- Attribute sind unveränderlich.
- Die in Liste usw. enthaltenen Objekte sind ebenfalls unveränderlich.
- Nachdem Sie List # add () verwendet haben, das Attribute von innen neu schreibt, erstellen Sie ein neues eigenes Objekt und geben Sie es zurück.
- Die Äquivalenz wird durch Abgleichen der Attribute bestätigt.
- In einigen Fällen können die Attribute variabel sein.
- ** [Achtung] ** Siehe Werksabschnitt
- Implementieren Sie equals () und hashCode () selbst
ValueObject-Übersicht
// ----Grundsätzlich das--------------------------
//Attribute sind unveränderlich
@Getter
public final class Task implements ValueObject {
private final String value;
private final Boolean done;
public Task(final String value) {
Validate.notNull(value);
this.value = value;
this.done = false;
}
public Task(final String value, final Boolean done) {
Validate.notNull(value);
Validate.notNull(done);
this.value = value;
this.done = done;
}
@Override
public boolean equals(final Object that) {
if (this == that) {
return true;
}
if (that == null || getClass() != that.getClass()) {
return false;
}
Task o = (Task) that;
if (!value.equals(o.value)) {
return false;
}
if (!done.equals(o.done)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = Objects.hashCode(value);
result = 31 * result + Objects.hashCode(done);
return result;
}
}
// ----Haben Sie ein Wertobjekt im Kind--------------------------
//Attribute sind unveränderlich
//Wenn es sich bei dem Attribut jedoch um ein Objekt wie List handelt, ist ein gewisser Einfallsreichtum erforderlich.
public final class TaskList implements ValueObjectList<TaskList, Task> {
private final List<Task> taskList;
public TaskList() {
taskList = Collections.unmodifiableList(new ArrayList<>());
}
public TaskList(final List<Task> list) {
Validate.notNull(list);
// unmodifiableList()Der Grund ist
//Liste, die durch Übergabe als Referenz zurückgegeben wird<Task>Stellen Sie sicher, dass der Inhalt nicht neu geschrieben werden kann
//Wenn Sie es neu schreiben möchten, möchten Sie die Verwendung des Konstruktors erzwingen.
//Da die endgültige Deklaration auch im Element nicht funktioniert, wird sie an dieser Stelle auch im deklarativen Sinne verwendet.
taskList = Collections.unmodifiableList(list);
}
/*
Collections.unmodifiableList()Der Grund für die Verwendung
Wenn Sie es nicht verwenden, wird der folgende Testcode nicht bestanden und Sie können nicht garantieren, dass sich die Attribute nicht ändern.
Wenn Sie taskList verwenden.set(0, after)UnsupportedOperationException tritt in auf
Wenn Sie das Element ArrayList neu schreiben möchten<>(taskList)Durch Übergabe an den Konstruktor von TaskList
Beim Umschreiben der Elemente kann garantiert werden, dass die Attribute unveränderlich sind (= ein anderes Objekt mit anderen Attributen, da der Konstruktor verwendet wird).
TaskList target = new TaskList();
Task before = new Task("Milch kaufen", false);
target.add(before);
assertEquals(1, target.getList().size());
//Überschreiben Sie den ersten Teil der abgerufenen Liste mit einem anderen Objekt
List<Task> taskList = target.getList();
Task after = new Task("Milch verkaufen", true);
taskList.set(0, after);
//Der überschriebene Betrag wird nicht in der Variablen berücksichtigt, bevor er vor dem Überschreiben gesichert wurde.
assertEquals(before.getFavorite(), target.getList().get(0).getFavorite());
*/
private static void accept(final Task w) {
}
public Optional<Task> find(final Task searchTask) {
Validate.notNull(searchTask);
Optional<Task> ret = Optional.empty();
for (Task existTask : getList()) {
if (existTask.equals(searchTask)) {
ret = Optional.of(existTask);
break;
}
}
return ret;
}
public List<Task> getList() {
return taskList;
}
public TaskList add(final Task... t) {
Arrays.stream(t).forEach(Validate::notNull);
List<Task> result = new ArrayList<>(taskList);
Arrays.stream(t).forEach(
v -> find(v).ifPresentOrElse(
TaskList::accept,
() -> result.add(v)
)
);
return new TaskList(result);
}
/*
Der Grund, mich zu regenerieren und zurückzukehren, ist
Internes Attribut ist Sammlungen#unmodifiableList()Es kann nicht geändert werden, da es sich nicht geändert hat.
Als Einschränkung List#add(Object):Wenn Sie es als Booleschen Wert verwenden, wird dies nicht aktualisiert und Sie erhalten keine neuen Werte.
*/
public TaskList remove(final Task... t) {
Arrays.stream(t).forEach(Validate::notNull);
List<Task> result = new ArrayList<>(taskList);
Arrays.stream(t).forEach(
v -> find(v).ifPresent(result::remove)
);
return new TaskList(result);
}
@Override
public boolean equals(final Object that) {
if (this == that) {
return true;
}
if (that == null || getClass() != that.getClass()) {
return false;
}
TaskList o = (TaskList) that;
if (!taskList.equals(o.taskList)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = Objects.hashCode(taskList);
result = 31 * result;
return result;
}
}
- ** Attribute und deren Inhalt werden grundsätzlich nicht umgeschrieben. Sollte wegwerfbar sein. ** **.
- Für zwei Personen mit demselben Nachnamen und demselben Namen in den Kundendaten können mit Value Object zwei Personen ein Objekt mit demselben Nachnamen und demselben Namen gemeinsam nutzen. Jedoch,,
-
- Wenn der Name einer Person geändert wird, kann sich auch der Name der anderen Person ändern! Um dies zu verhindern und das Objekt sicher zu teilen, muss das Objekt unveränderlich sein. Dies bedeutet, dass Sie es nur ändern können, indem Sie es vollständig ersetzen. (Evans Nr. 2533-2535) *
- In einigen Fällen kann es variabel sein.
-
- Wenn der Wert häufig aktualisiert wird, wenn die Kosten für das Erstellen oder Zerstören eines Objekts hoch sind, wenn beim Ersetzen einer Instanz ein Problem mit dem Clustering auftritt oder wenn der Wert nicht gemeinsam genutzt wird (Verbesserung des Clustering oder aus anderen technischen Gründen) Einschließlich) kann variabel sein. (Kato Blog) *
- ** Die Gleichwertigkeit wird durch den Abgleich aller Attribute garantiert **
- Es gibt keine explizite Beschreibung in Evans ...
-
- Wenn ein Wertobjekt instanziiert wird, ist es ein Designelement, das nur darauf ankommt, was es ist, und es spielt keine Rolle, wer oder wer es ist. (Evans Nr. 2483-2484) *
-
- Wertobjekte können aus einer Kombination anderer Objekte bestehen. (Evans Nr. 2490) *
- Beziehung zwischen Fenster (Entität), Fensterstil (Wertobjekt) und Baustoff (Wertobjekt)
- ** * Mit zunehmender Anzahl von Attributen explodieren die Kosten, wenn Sie einige Attribute neu schreiben möchten. Es soll ein Nachteil von unveränderlichen Objekten sein. ** **.
- Als Problemumgehung wird ValueObjectBuilder implementiert.
――Es gibt kein Konzept für Builder in DDD, aber es scheint, dass Sie es neben der Fabrik in dem Sinne anordnen können, dass es den konkreten Erzeugungsprozess verbirgt.
factory
- Charakteristisch
- Ein Objekt, das die Erzeugung einer Entität übernimmt
- Bestimmen Sie, ob die Methode nach Kosten statisch gemacht werden soll
- Wenn es einfach ist, eine Entität zu erstellen, wird sie möglicherweise nicht erstellt.
- Manchmal führen wir das Klonen von Entitäten einschließlich der Variablen ValueObject durch
Werksübersicht
public class TodoFactory implements EntityFactory<Todo> {
@Override
public Todo create() {
return create("");
}
@Override
public Todo create(final EntityIdentifier<Todo> identifier) {
Validate.notNull(identifier);
Title t = new Title("");
DueDate d = new DueDate();
TaskList l = new TaskList();
Memo m = new Memo();
Favorite f = new Favorite();
return new Todo(identifier, t, d, l, m, f);
}
/**
* {@link Title}Angeben{@link Todo}erstellen
*
* @param title
* @return
*/
public Todo create(final String title) {
Validate.notNull(title);
EntityIdentifier<Todo> identifier = new DefaultEntityIdentifier<>(Todo.class, UUID.randomUUID());
Title t = new Title(title);
DueDate d = new DueDate();
TaskList l = new TaskList();
Memo m = new Memo();
Favorite f = new Favorite();
return new Todo(identifier, t, d, l, m, f);
}
}
-
- Repositories und Fabriken sind keine eigenständigen Domänen (Evans Nr. 3069-3070) *
- ** Objekt, das das Domänenmodell generiert **
-
- Konzentrieren Sie sich auf den Beginn des Lebenszyklus und verwenden Sie Fabriken, um komplexe Objekte und Aggregate zu erstellen und zu rekonstruieren. (Evans Nr. 3066-3067) *
- ** Wenn es einfach zu generieren ist und es keine Variation gibt, müssen Sie es nicht vorbereiten. ** **.
- ** In einigen Fällen muss das Domain-Modell dupliziert werden **
-
- Da es sich bei der Klonmethode um eine flache Kopie handelt, vergessen Sie möglicherweise, eine tiefe Kopie unter Berücksichtigung variabler Objekte zu erstellen, oder Sie verpassen die Initialisierung, weil sie nicht endgültig ist. (Kato Blog) *
- ** Es geht aus der Geschichte des Defekts der Klonmethode hervor, die die Überschreibungsmethode enthält **
- Weisen Sie auch darauf hin, dass wenn Sie versuchen, kompliziert mit der Klonmethode des Domänenmodells zu duplizieren, dies gegen SRP verstößt.
- Wenn jedoch "wenn dies nicht der Fall ist" und "ValueObject ein unveränderliches Objekt ist", müssen Sie nicht darüber nachdenken. Das unveränderliche Wertobjekt erlaubt keine elementaren Änderungen und keine Setter. Da "Wertänderung = Erstellung neuer Objekte", ist das Ergebnis nach der Wertänderung das gleiche wie zum Zeitpunkt der Tiefenkopie, auch wenn der Klon eine flache Kopie ist.
- "Wenn Sie ein variables Wertobjekt erstellen" ist ein zu berücksichtigender Punkt.
repository
-
- Repositories und Fabriken sind keine eigenständigen Domänen (Evans Nr. 3069-3070) *
- ** Objekt, das das Domänenmodell beibehält **
-
- Das Repository kapselt die riesige Infrastruktur, die für den Zugriff erforderlich ist, und bietet gleichzeitig die Möglichkeit, auf persistente Objekte in der Mitte und am Ende des Lebenszyklus zuzugreifen. (Evans Nr. 3067-3069) *
- ** Objekt zum Wiederherstellen des Domänenmodells **
-
- Wie viele Seiten speziell? * *
Anhäufung
-
- Der durch die Aggregation begrenzte Umfang gibt an, inwieweit die invariante Bedingung beibehalten werden muss. (Evans Nr. 3073-3074) *
- ** Objekt, das das Domänenmodell synthetisiert hat **
-
- Wie viele Seiten speziell? * *
- ** Transaktionsgrenze **
-
- Wie viele Seiten speziell? * *
domain service
-
- Ein Dienst ist eine Operation, die als unabhängige Schnittstelle im Modell bereitgestellt wird und den Status nicht wie eine Entität und ein Wertobjekt kapselt. (Evans Nr. 2632-2634) *
-
- Nur aus der Perspektive definiert, was dem Kunden angetan werden kann (Evans Nr. 2636-2637) *
-
- Argumente und Ergebnisse sollten Domänenobjekte sein (Evans Nr. 2641) *
-
- Exzellenter Service hat drei Eigenschaften. (Evans Nr. 2645-2648) *
- Die Operation beinhaltet das Konzept einer Domäne, die kein ** natürlicher Bestandteil einer Entität oder eines Wertobjekts ** ist.
- Die Schnittstelle wird aus der Perspektive ** anderer Elemente des Domänenmodells ** definiert.
- Es gibt keinen ** Zustand in der Operation **.
-
- Technische Dienstleistungen sollten keine geschäftliche Bedeutung haben. (Evans Nr. 2673) *
- Aus der Geschichte der Modellierung der Geschäftslaufzeit einer Bank
―― Mit anderen Worten, ein aussagekräftiges Geschäftsverhalten sollte auf das Domänenmodell ausgerichtet sein.
- ** Beim Umgang mit mehreren Domain-Modellen und bei der Realisierung von Geschäftslogik **
-
- Die Funktion zum Überweisen von Geldern von einem Konto auf ein anderes ist ein Domänendienst. (Evans Nr. 2680-2681) *
- ** Wenn es nicht für die Geschäftslogik des Domain-Modells geeignet ist **
-
- Es wird schwierig zu handhaben, wenn Sie die Operation "Übertragen" in das Kontoobjekt einfügen. Diese Operation umfasst zwei Konten und einige globale Regeln (Evans Nr. 2684-2685) *
- ** Erstellen Sie jedoch nicht so viele Domänendienste wie möglich **
-
- Wie viele Seiten speziell? * *
application service
- ** Bei der Realisierung des Anwendungsfalls der Anwendung anhand des Domänenmodells **
-
- Wenn eine Bankanwendung beispielsweise Transaktionen konvertieren und zur Analyse in eine Tabellenkalkulationsdatei exportieren kann, ist der Export ein Anwendungsdienst. Im Bankbereich hat "Dateiformat" keine Bedeutung und Geschäftsregeln sind nicht beteiligt. (Evans Nr. 2677-2680) *
-** Sehr dünn **
――Es bedeutet, dass Sie die Ebene nicht durchlaufen. Wenn man sich die obige Beschreibung ansieht, ist sie überhaupt nicht dünn, wie zum Beispiel das Erstellen einer Tabelle.
-
- Wie viele Seiten speziell? * *
infrastructure service
-
- Die meisten in der Literatur diskutierten Dienste sind rein technisch und diese Dienste gehören zur Infrastrukturschicht. (Evans Nr. 2662-2663) *
-
- Wenn Ihr Kontostand ein bestimmtes Limit unterschreitet, können Sie Ihren Kunden eine E-Mail senden. Dieses E-Mail-System wird von einer Schnittstelle gekapselt, die in einigen Fällen alternative Benachrichtigungsmethoden enthalten kann. Diese Schnittstelle ist ein Dienst der Infrastrukturschicht (Evans Nr. 2664-2667) *
interfaces
--GET / POST / PUT / DELETE und andere Orte zum Empfangen
- Ist das nicht der Socket-Anschluss?