[JAVA] Was ist die Darstellung von Domänenwissen im [DDD] -Modell?

DDD serialisierter Artikel

Überblick

Was sagt Eric Evans zur Definition von DDD In diesem Artikel habe ich geschrieben, dass die Definition der domänengetriebenen Entwicklung wie folgt lautet:

  1. Konzentrieren Sie sich auf die Kernkomplexität und die Möglichkeiten der Domäne
  1. Untersuchen Sie Modelle in Zusammenarbeit mit Domain- und Software-Experten
  2. Schreiben Sie eine Software, die diese Modelle explizit darstellt
  3. Sprechen Sie in einer allgegenwärtigen Sprache in einem begrenzten Kontext

Wie wird das verbalisierte Modell des Domänenwissens letztendlich im Code dargestellt?

Unveränderlicher Zustand

Wenn Sie über geschäftliche Einschränkungen nachdenken, denken Sie zunächst an *** invariante Bedingungen ***.

Invariante Bedingung: Eine Bedingung, die während der gesamten Lebensdauer des Modells konsistent sein muss.

Es gibt verschiedene Namen wie Spezifikationen und Einschränkungsbedingungen, aber der Begriff invariante Bedingung wird als Begriff für DDD verwendet, sodass wir ihn vereinheitlichen werden.

Ein Modell, das kein Domänenwissen darstellt

Angenommen, Sie möchten eine Aufgabe in einer Geschäftsanwendung modellieren. Nachdem ich die Anforderungen definiert hatte, stellte ich fest, dass die folgenden invarianten Bedingungen erfüllt sein müssen.

Lassen Sie uns dies implementieren.

@Entity
public class Task {
    @Id
    @GeneratedValue
    private Long id;
    private TaskStatus taskStatus;
    private String name;
    private LocalDate dueDate;
    private int postponeCount;
    public static final int POSTPONE_MAX_COUNT = 3;

    public Task() {
    }

    /*
     *Setter für alle Artikel
     */
    public void setId(Long id) { this.id = id; }
    public void setTaskStatus(TaskStatus taskStatus) { this.taskStatus = taskStatus; }
    public void setName(String name) { this.name = name; }
    public void setDueDate(LocalDate dueDate) { this.dueDate = dueDate; }
    public void setPostponeCount(int postponeCount) { this.postponeCount = postponeCount; }

    /*
     *Besser für alle Artikel
     */
    public Long getId() { return this.id; }
    public String getName() { return this.name; }
    public TaskStatus getTaskStatus() { return this.taskStatus; }
    public LocalDate getDueDate() { return this.dueDate; }
    public int getPostponeCount() { return this.postponeCount; }
    
    /*
     *Eine Methode, die kaum Verhalten hat
     */
    public boolean canPostpone() { return this.postponeCount < POSTPONE_MAX_COUNT; }
}
public enum TaskStatus {
    UNDONE, DONE
}
public class TaskApplication {
    @Autowired
    private TaskRepository taskRepository;
    
    public void createTask(String name, LocalDate dueDate) {
        if (name == null || dueDate == null) {
            throw new IllegalArgumentException("Erforderliche Elemente sind nicht festgelegt");
        }
        Task task = new Task();
        task.setTaskStatus(TaskStatus.UNDONE);
        task.setName(name);
        task.setDueDate(dueDate);
        task.setPostponeCount(0);
        taskRepository.save(task);
    }
    
    public void postponeTask(Long taskId) {
        Task task = taskRepository.findById(taskId);
        if (!taks.canPostpone()) {   	
            throw new IllegalArgumentException("Die maximale Anzahl von Verschiebungen wurde überschritten");
        }
        task.setDueDate(task.getDueDate().plusDays(1L));
        task.setPostponeCount(task.getPostponeCount() + 1);
        taskRepository.save(task);
    }
    //Die Abschlussverarbeitung entfällt
}

Es ist fertig! Es sieht so aus, als würde es die Anforderungen erfüllen. In der TaskApplication-Klasse scheint es kein Problem mit der Erstellung und Aktualisierung von Aufgaben zu geben. Lassen Sie es uns jetzt veröffentlichen.

Einige Wochen nach der Veröffentlichung hat jedoch ein Ingenieur, der mit dem Geschäft nicht vertraut ist, den folgenden Code implementiert.

public class AnotherTaskApplication {
    @Autowired
    private TaskRepository taskRepository;
    
    public void createDoneTask(String name, LocalDate dueDate) {
        Task task = new Task();
        task.setTaskStatus(TaskStatus.DONE);  //× Aufgabengenerierung im abgeschlossenen Zustand
        task.setPostponeCount(-1); //× Die Anzahl ist ein Minus
        taskRepository.save(task);
    }

    public void changeTask(Long taskId, String name, LocalDate dueDate, TaskStatus taskStatus) {
        Task task = taskRepository.findById(taskId);
        task.setName(name); //× Ändern Sie den Aufgabennamen, der nicht geändert werden soll
        task.setDueDate(dueDate); //× Stellen Sie das Fälligkeitsdatum beliebig mit dem Eingabewert ein und ignorieren Sie die Anzahl der Verschiebungen
        task.setTaskStatu(taskStatus); //× Aufgaben können auf unvollständig zurückgesetzt werden
        taskRepository.save(task);     }
}

Es ist erstaunlich, dass ich die unveränderlichen Bedingungen zerstören konnte.

Das Problem besteht darin, dass die AnotherTaskAPplication-Klasse erstellt und implementiert wird, sodass die Person, die die ursprüngliche TaskApplication-Klasse implementiert hat, nicht bemerkt hat, dass eine solche Verarbeitung in einer anderen Klasse geschrieben wurde. (Lassen Sie uns das Überprüfungssystem vorerst verlassen)

In diesem Beispiel wird die Spezifikation von Application ausgedrückt, aber das Task-Modell selbst, das in allen Elementen Setter / Getter enthält, kann nichts ausdrücken.

Dieser Zustand wird als *** Domänenmodellanämie *** bezeichnet.

Dieses Objekt, das keine invarianten Bedingungen mehr darstellen kann, ist kein Modell mehr, sondern lediglich ein *** Datenmodell ***, das ein relationales Modell auf das Objekt projiziert. Und eine solche Anwendung kann als *** Transaktionsskript *** bezeichnet werden.

Wie können wir also das Modell dazu bringen, invariante Bedingungen darzustellen?

Ein Modell, das Domänenwissen ausdrückt

@Entity
class Task {
    @Id
    @GeneratedValue
    private Long id;
    private TaskStatus taskStatus;
    private String name;
    private LocalDate dueDate;
    private int postponeCount;
    private static final int POSTPONE_MAX_COUNT = 3;

    /*
     *Konstruktor: Stellt eine invariante Bedingung beim Erstellen einer Entität dar
     */
    public Task(String name, LocalDate dueDate) {
        if (name == null || dueDate == null) {
            throw new IllegalArgumentException("Erforderliche Elemente sind nicht festgelegt");
        }
        this.name = name;
        this.dueDate = dueDate;
        this.taskStatus = TaskStatus.UNDONE;
        this.postponeCount = 0;
    }

    /*
     *Zustandsübergangsmethode: Stellt eine invariante Bedingung dar, die sich auf den Zustandsübergang einer erstellten Entität bezieht
     */
    public void postpone() {
        if (postponeCount >= POSTPONE_MAX_COUNT) {
            throw new IllegalArgumentException("Die maximale Anzahl von Verschiebungen wurde überschritten");
        }
        dueDate.plusDays(1L);
        postponeCount++;
    }
    public void done() {
        this.taskStatus = TaskStatus.DONE;
    }
    
    //Es gibt keinen Namensgeber, daher können Sie den Namen nicht ändern

    /*
     *Getter, Zustandserfassungsmethode
     */
    public Long getId() { return id; }
    public String getName() { return name; }
    public LocalDate getDueDate() { return dueDate; }
    public boolean isUndone() { return this.taskStatus == TaskStatus.UNDONE; }
    public boolean canPostpone() { return this.postponeCount < POSTPONE_MAX_COUNT; }
}

//Im selben Paket wird enum als Paket privat definiert
enum TaskStatus {
    UNDONE, DONE
}
public class TaskApplication {
    @Autowired
    private TaskRepository taskRepository;

    public void createTask(String name, LocalDate dueDate) {
        Task task = new Task(name, dueDate); //Aufgabeninstanzen werden immer mit unveränderlichen Bedingungen erstellt
        taskRepository.save(task);
    }

    public void postpone(Long taskId) {
        Task task = taskRepository.findById(taskId);
        task.postpone(); //Eine Verarbeitung, die unveränderliche Bedingungen zerstört, kann nicht aus der Anwendung für das erfasste Objekt geschrieben werden
        taskRepository.save(task);
    }
    //Die Abschlussverarbeitung entfällt
    //Die Validierung entfällt
}

Als der Prozess des Auferlegens invarianter Bedingungen auf das Modell übertragen wurde ... wurde ein sehr ausdrucksstarkes Modell erstellt! !!

Die invariante Bedingung wird vom Konstruktor des Task-Modells und der Zustandsübergangsmethode wunderbar ausgedrückt.

Einige der Vorteile dieses Designs sind:

Es ist eine gute Sache.

Wenn Sie dies schreiben, können Sie sagen, dass das *** Modell das Wissen über die Domäne *** ausdrückt.

Rückblick auf die Definition

Lassen Sie uns auf die domänengesteuerte Definition zurückblicken.

  1. Konzentrieren Sie sich auf die Kernkomplexität und die Möglichkeiten der Domäne
  1. Untersuchen Sie Modelle in Zusammenarbeit mit Domain- und Software-Experten
  2. Schreiben Sie eine Software, die diese Modelle explizit darstellt
  3. Sprechen Sie in einer allgegenwärtigen Sprache in einem begrenzten Kontext

Mit dem obigen Beispiel konnten wir Software schreiben, die das Modell darstellt.

Dieses Design allein hat große Vorteile gehabt, aber das Domain-Fahren endet hier nicht.
Wie wird sich ein solches Modell verhalten und wie wird es sich ändern, wenn die Spezifikationen geändert werden? Indem wir die Definition von Wörtern mit Experten für Unternehmen (Domänen) abgleichen, verfolgen wir immer das neueste und ausdrucksstärkere Modell und folgen dem Code.

Dies ist buchstäblich *** domänenmodellgetriebene Entwicklung ***.

Ich hoffe, Sie lesen diesen Artikel und denken: "Oh, vielleicht DDD?" Ich werde auch in Zukunft Artikel schreiben, um DDD zu verbreiten. Bitte bleiben Sie mit mir in Kontakt, wenn Sie möchten.

Recommended Posts

Was ist die Darstellung von Domänenwissen im [DDD] -Modell?
Was ist der Unterschied zwischen den Verantwortlichkeiten der Domänenschicht und der Anwendungsschicht in der Zwiebelarchitektur [DDD]?
'% 02d' Was ist der% von% 2?
Was ist @Override oder @SuppressWarnings ("SleepWhileInLoop") vor der Funktion? ?? ??
Was ist ein Test? ・ Über die Wichtigkeit eines Tests
Was ist die Hauptmethode in Java?
Wie ist die Datenstruktur von ActionText?
Was ist JSP? ~ Lassen Sie uns die Grundlagen von JSP kennen !! ~
Was ist das Java Servlet / JSP MVC-Modell?
Ebean.update () wird im geerbten Modell nicht ausgeführt.
Rufen Sie den ersten Wochentag im aktuellen Gebietsschema ab (welcher Tag ist heute?)
Was ist ein MVC-Modell?
In Time.strptime ist% j (Gesamtdatum des Jahres)
Was ist domänengesteuertes Design, das versucht, [DDD] zu lösen?
[DDD] Was ist die am besten zugängliche Architektur, um mit domänengesteuertem Design zu beginnen?
Was ist die Zupfmethode?
Überprüfen Sie die Grundkenntnisse von Rubin, die oft vergessen werden
Was ist die BufferedReader-Klasse?
Wofür ist der Konstruktor?
Was ist die Initialisierungsmethode?
Die Geschichte, dass ein Modell keine "korrekte Darstellung der realen Welt" ist / die Notwendigkeit eines begrenzten Kontexts
Möchten Sie wissen, was Ruby n die Potenz von 2 ist? (Machturteil von 2)
Was tun, wenn Cloud 9 im Rails-Lernprogramm voll ist?
Die in /lib/calendars.properties von Java jre festgelegte Millisekunde ist UTC
Das Problem, dass das Attribut des Benutzermodells in ActionMailer gleich Null wird
[Referenzbeispiel] Das Urheberrecht wird im Kommentar des Quellcodes beschrieben.
Was ist ein Ausschnitt in der Programmierung?
Was ist es? ~ 3 Arten von "Nein" ~
Was ist @Autowired im Spring Boot?
Reihenfolge der Verarbeitung im Programm
Was für eine Methode ist define_method?
Was sind die Regeln in JUnit?
Was tun, wenn das Präfix c in JSP nicht gebunden ist?
Code des Teils, in dem server.servlet.session.timeout in spring.session.timeout im spring-boot2-System festgelegt ist
Was war keine faire Verwendung der Java-API-Umleitung unter Android?
Fehler, wenn das in SpringWebFlux verwendete Mitglied der Entity-Klasse endgültig ist
Überprüfung von "seltsamem Java" und Java-Wissen, das in Java Bronze oft vergessen wird
Was ist CHECKSTYLE: OFF in der Java-Quelle? Checkstyle zu wissen von
[Anfänger] Was ist Docker überhaupt? Leicht verständliche Erklärung aus den Grundlagen!
Was ist eine Klasse in der Java-Sprache (3 /?)
Holen Sie sich das Ergebnis von POST in Java
Spring Autowired wird im Konstruktor geschrieben
Was ist Swift? Ergebnisse in 3 Wochen erhalten
Was ist der beste Migrationsdatentyp? ??
[Technischer Hinweis] Was ist "include" in Ruby?
Die Identität der Schienenparameter [: id]
Was ist das beste Lesen von Dateien (Java)
Was ist eine Klasse in der Java-Sprache (1 /?)
Was ist eine Klasse in der Java-Sprache (2 /?)
Was sind die aktualisierten Funktionen von Java 13
Wofür ist das Fassadenmuster nützlich?
Die Geschichte des Schreibens von Java in Emacs