[JAVA] Was ich dachte, als ich den Eingabewert des Benutzers an die Service-Klasse übergab

Was ich dachte, als ich den Eingabewert des Benutzers an die Service-Klasse übergab

Fazit

Die Konvertierung zwischen der Form-Klasse, die den Eingabewert erhält, und der Entität ist schwierig, daher habe ich mir vier Lösungen überlegt.

  1. Machen Sie jeden Eingabewert zu einem Argument der Service-Methode
  2. Machen Sie Objekte in sinnvollen Einheiten
  3. Verwandeln Sie die Service-Methode in eine Form-Klasse
  4. Machen Sie das Serviceargument zu einer Schnittstelle

Ich denke, die Lösungen, die verwendet werden könnten, sind 2 und 4. Dieses Mal haben wir es in 4 basierend auf der internen Kultur gelöst.

Umgebung

Annahme: Die Konvertierung zwischen der Formularklasse, die den Eingabewert erhält, und der Entität ist schwierig

Ich habe mit Entity für die Persistenz in der Form-Klasse wie folgt konvertiert.

PurchaseForm.java


@Data
public class PurchaseForm {
  @NotNull
  private Integer commodityId;
  @NotNull
  private Integer count;

  /**
   *Generieren Sie eine Entität basierend auf dem Eingabewert
   */
  public Purchase toEntity() {
    //Kürzung
  }
}

Wenn es sich um eine einfache Konvertierung handelt, gibt es überhaupt kein Problem, aber wenn es sich um das folgende Muster handelt, stirbt es.

Wenn die zu konvertierende Klasse eine komplexe Klassenstruktur hat

Mit zunehmender Menge an Code im Programm, der in Entity konvertiert werden soll, wird die Sichtbarkeit des Quellcodes schlechter. Besonders wenn versucht wird, eine untergeordnete Klasse der zu generierenden Klasse zu generieren, ist dies ziemlich schmerzhaft. In den Projekten, an denen ich beteiligt bin, verlasse ich mich bei der Nummerierung des Primärschlüssels häufig auf AUTO INCREMENT. Daher ist es zwangsläufig erforderlich, den Primärschlüssel und den externen Schlüssel durch die Serviceklasse zu ergänzen. Ein Problem ist in mehrere Klassen unterteilt, z. B. das Initialisieren eines Teils mit Form und das Initialisieren des Restes mit der Service-Klasse, und es wird schwierig, dem Quellcode zu folgen.

PurchaseForm.java


@Data
public class PurchaseForm {
  //Kürzung
  /**
   *Generieren Sie eine Entität basierend auf dem Eingabewert
   */
  public Purchase toEntity() {
    //Dies
    //Dies
    //Aber
    //Mich
    //Tsu
    //Chi
    //Ja
    //Lange
    //ich
    //Wann
    //Fazit
    //Struktur
    //Einer
    //Et al.
    //ich
  }
}

Es ist schmerzhaft, wenn für die Entitätsgenerierung viele Argumente erforderlich sind

Wenn Sie bisher ein beständiges Objekt als Argument benötigen, sollten Sie es nicht mehr in die Form-Klasse konvertieren.

PurchaseForm.java


@Data
public class PurchaseForm {
  //Kürzung
  /**
   *Generieren Sie eine Entität basierend auf dem Eingabewert
   *Wenn Sie eine große Anzahl von persistierten Objekten als Argumente verwenden, fragen Sie sich, ob Sie hier eine Entität erstellen müssen.
   */
  public Purchase toEntity(Hoge hoge, Fuga Futa, ... etc) {
    //Kürzung
  }
}

Mögliche Lösungen

1. Machen Sie jeden Eingabewert zu einem Argument der Service-Methode

Ich habe es versucht, aber es ist schmerzhaft. Denn mit zunehmendem Eingabewert von Form steigt die Anzahl der Argumente.

PurchaseController.java


@Controller
@RequireArgsConstructor
public class PurchaseController {
  public final PurchaseService purchaseService;
  public ModelAndView create(@ModelAttribute @Valid PurchaseForm form, BindingResult result) {
    // check
    
    // Ah...!
    purchaseService.purchase(form.input1, form.input2, form.input3, form.input4, form.input5,...);
  }
}

2. Machen Sie Objekte in sinnvollen Einheiten

PurchaseController.java


@Controller
@RequireArgsConstructor
public class PurchaseController {
  public final PurchaseService purchaseService;
  public ModelAndView create(@ModelAttribute @Valid PurchaseForm form, BindingResult result) {
    // check
    
    purchaseService.purchase(new Hoge(form.input1, form.input2), new Fuga(form.input3, form.input4, form.input5),...);
  }
}

Dies hat den Vorteil, dass Sie das Argumentobjekt vorkonditionieren können, um die Wertintegrität sicherzustellen. Voraussetzungen werden im Konstruktor geprüft.

3. Verwandeln Sie die Service-Methode in eine Form-Klasse

Es ist sehr einfach, aber es ist ein wirklich schlechtes Muster! Es ist schwierig für die Abhängigkeitsrichtung, Form ← Service zu sein. Form ist eng mit Bildschirmspezifikationen verbunden, daher möchte ich nicht, dass die Seite der Geschäftslogik von Form abhängt. Es ist seltsam, dass die Serviceklasse geändert werden muss, da sich die Bildschirmspezifikationen geändert haben, obwohl sich die Geschäftslogik nicht geändert hat.

PurchaseController.java


@Controller
@RequireArgsConstructor
public class PurchaseController {
  public final PurchaseService purchaseService;
  public ModelAndView create(@ModelAttribute @Valid PurchaseForm form, BindingResult result) {
    // check
    
    // Fuck...!
    purchaseService.purchase(form);
  }
}

Dies wurde mit dem vierten Plan gelöst.

4. Machen Sie das Serviceargument zu einer Schnittstelle

Zuerst habe ich die Service- und Argumentschnittstelle in einem Paket in der Nähe der Domäne definiert.

PurchaseService.java


@Service
public class PurchaseService {
  public void purchase(PurchaseRequest request) {}
}

PurchaseRequest.java


public interface PurchaseRequest {
  String getInput1();
  Integer getInput2();
  // etc...
}

Dann habe ich die Wertvalidierung in eine andere Implementierungsklasse geschrieben.

PurchaseForm.java


public class PurchaseForm implements PurchaseRequest {
  @NotEmpty
  private String input1;
  @NotNull
  private Integer input2;
  public String getInput1() {
    return input1;
  }
  public Integer getInput2() {
    return input2;
  }
}

Das Schöne an dieser Implementierungsmethode ist, dass sie den in der Service-Klasse verwendeten Wert auf der Ebene in der Nähe der Domäne deklariert, sodass sie nicht auf der Bildschirmseite geändert werden kann. Wenn sich Input und Output als Geschäftskonzept ändern, muss dies natürlich überprüft werden, aber einige Unterschiede können auf der Bildschirmseite kontrolliert werden.

Ergänzung

Besser noch, ich denke, es ist gut, den Eingabewert vom Bildschirm an den auf der Domänenseite definierten Typ zu binden. Ich denke, dass es oft als die optimale Lösung bezeichnet wird. Dies ist die Lösung, die wir in unserem Projekt ohne eine Kultur der Implementierung der Validierung auf domänenseitigen Entitäten oder der semantischen Erfassung und Implementierung von Werttypen durchgeführt haben.

Unerwartete Nebenwirkung

Danach habe ich beschlossen, eine API zu erstellen, um den Kaufpreis basierend auf den Informationen zu den gekauften Produkten und der Menge zu berechnen. Wenn Sie eine Methode namens "PurchaseService # berechnePreis (CalculatePriceRequest)" definieren und "PurchaseRequest" "CalcululatePriceRequest" erben lassen, können Sie den Kaufpreis auch mit einem Argument vom Typ "PurchaseRequest" problemlos berechnen. Das ist richtig, da die Produktkaufanfrage Informationen zu Produkt und Menge enthält, ist es ganz natürlich, den Kaufpreis anhand dieser Informationen berechnen zu können.

Schließlich

Ich mache viel Versuch und Irrtum, aber ich denke, dass ich in der Lage sein sollte, die Hand der Montagemethode zu erhöhen, indem ich die Muster analysiere, die funktionierten und die Muster, die nicht funktionierten. Insbesondere entwickle ich häufig WEB-Anwendungen, daher möchte ich über verschiedene E / A mit dem Bildschirm nachdenken.

Recommended Posts

Was ich dachte, als ich den Eingabewert des Benutzers an die Service-Klasse übergab
Wovon ich bei der Einführung der JNI-Bibliothek süchtig war
Woran ich dachte, als ich anfing, von Java nach Kotlin zu migrieren
Memorandum: Wovon ich süchtig war, als ich auf die Accounting Freee API traf
Aufgenommen, weil ich süchtig nach der Standardeingabe der Scannerklasse war
Was ich versucht habe, als ich alle Felder einer Bohne bekommen wollte
Was tun, wenn der Wert im zweiten getSubmittedValue () in JSF Validator null wird?
Achten Sie bei Verwendung des Float-Typs auf die Grenzprüfung des Eingabewerts
Was ich getan habe, als ich Java zu Kotlin konvertiert habe
Ich möchte den Wert in Ruby erhalten
Was tun, wenn das SSL-Zertifikat abgelaufen ist?
Was ich dachte, als ich anfing, als Ingenieur zu arbeiten
Was muss ich tun, um die aktualisierte Docker-Datei neu zu laden?
Was ich beim Update auf Spring Boot 1.5.12 behoben habe ・ Wovon ich süchtig war
Was ich mit der Redmine REST API süchtig gemacht habe
Ich möchte dem select-Attribut einen Klassennamen geben
Ich möchte die Liste der Klassen unter dem Paket rekursiv durchsuchen
Ich möchte mehrere Rückgabewerte für das eingegebene Argument zurückgeben
Die Geschichte, nach der ich beim Einrichten von STS süchtig war
Ich möchte die Notwendigkeit des Testens beurteilen, indem ich die Unterschiede der Klassendateien beim Refactoring von Java vergleiche.
Ich möchte den Rahmen des Textfelds rot machen, wenn ein Eingabefehler auftritt
Als ich versuchte, meinen eigenen Dienst auszuführen, schlug dies fehl und ich schraubte ihn in den Taskplaner
9 Entspricht dem Rückgabewert
Was ist die BufferedReader-Klasse?
Eingabe in die Java-Konsole
[java] Was ich getan habe, als ich Listen in meiner eigenen Klasse verglichen habe
Ich habe versucht zusammenzufassen, was bei der Site-Java-Ausgabe gefragt wurde.
Was tun, wenn die Änderungen im Servlet nicht berücksichtigt werden?
So zeigen Sie 0 auf der linken Seite des Standardeingabewerts an
So geben Sie den Wert aus, wenn sich ein Array im Array befindet
Was ich getan habe, als die DB nicht mit Docker-Compose gestartet wurde
Ich habe versucht, die Fehlermeldung beim Ausführen von Eclipse (Java) zu übersetzen.
Was ich bei der Migration von der Spring Boot 1.4-Serie zur 2.0-Serie getan habe
Einfache Möglichkeit zum Erstellen einer Zuordnungsklasse bei Verwendung der API
Wovon ich süchtig war, als ich die Google-Authentifizierung mit Rails implementierte
Ich möchte die Eingabe begrenzen, indem ich den Zahlenbereich einschränke
Was ich bei der Migration von der Spring Boot 1.5-Serie zur 2.0-Serie getan habe
Ich möchte den Wert von Attribute in Selenium of Ruby ändern