[JAVA] Erstellen eines Beispielprogramms mit dem Problem eines Datenbankspezialisten für DDD-Verbesserung 2

Einführung

Wie beim letzten Mal ist dies ein überarbeiteter Artikel zum Erstellen eines Beispielprogramms mit DDD unter Verwendung des Problems von Datenbankspezialisten.

Ich habe ein Beispielprogramm mit dem Problem des Datenbankspezialisten für domänengesteuertes Design erstellt Erstellen eines Beispielprogramms mit dem Problem eines Datenbankspezialisten mit DDD, Verbesserung 1

Dieses Mal habe ich versucht, den Teil, der sich auf den Endpunkt "Einzahlung" bezieht, umzugestalten.

Der Quellcode lautet hier Die Version zum Zeitpunkt der Veröffentlichung dieses Artikels ist Tag 1.2.

Umgestaltung des Siegerurteils der Lotterie durch mehrstufige Lotterie

Im ersten Artikel gepostet

Bei einer mehrstufigen Lotterie führt die Beurteilung des Gewinns der Lotterie von außerhalb des Eintrags dazu, dass die Logik in die Anwendung gelangt. Sie werden es vor allem später bereuen, wenn die Anzahl der Stufen zunimmt. Es heißt nicht, dass es zwei Stufen gibt, und wenn Sie es auf zwei Stufen erhöhen, werden Sie aufgefordert, es sofort um eine weitere Stufe zu erhöhen.

Normalerweise habe ich das kommentiert. Ich habe es versucht.

Lassen Sie uns zunächst das Lotterieergebnis des Lotterie-Teilnahmeantrags einschließlich des Ergebnisses der mehrstufigen Lotterie abrufen, es in der Sammlung der ersten Klasse registrieren und es ermöglichen, festzustellen, ob Sie aus der Sammlung der ersten Klasse gewonnen haben oder nicht. Es war.

LotteryEntryResults.java


public class LotteryEntryResults {

  private List<LotteryEntryResult> list;

  public LotteryEntryResults(List<LotteryEntryResult> list) {
    this.list = list;
  }

  /**
   *Gibt zurück, ob Sie gewonnen haben.Gibt true zurück, wenn gewonnen wird.
   */
  public boolean winning() {

    for (LotteryEntryResult result : list) {
      if (result.lotteryResult == LotteryResult.winning) {
        return true;
      }
    }

    return false;
  }
}

MybatisLotteryEntryResultRepository.java


  @Override
  public LotteryEntryResults findLotteryEntryResults(
      FestivalId festivalId,
      MemberId memberId,
      EntryId entryId) {

    List<LotteryEntryResult> resultList = new ArrayList<>();

    EntryId targetEntryId = entryId;
    while (true) {
      LotteryEntryResult lotteryEntryResult = lotteryEntryResultMapper.selectLotteryEntryResult(
          festivalId, memberId, targetEntryId);
      if (lotteryEntryResult == null) {
        break;
      }

      resultList.add(lotteryEntryResult);

      EntryDto entryDto = entryMapper.selectEntry(festivalId, targetEntryId);
      EntryId followingEntryId = entryDto.followingEntryId();
      if (followingEntryId == null) {
        break;
      }
      targetEntryId = followingEntryId;
    }

    return new LotteryEntryResults(resultList);
  }

Ich wurde gebeten, die rekursive Verarbeitung zu versuchen, aber ich hatte noch nie eine rekursive Verarbeitung mit SQL durchgeführt. Diesmal habe ich es also matschig versucht: heat_smile:

Infolgedessen war es möglich, das Siegerurteil der Lotterie von der Anwendungsschicht auf die Domänenschicht in der mehrstufigen Lotterie zu verschieben. Darüber hinaus ist es jetzt möglich, mehrstufige Lotterien mit drei oder mehr Stufen durchzuführen.

** Vor dem Refactoring **

PaymentCommandService.java



    Entry entry = entryRepository.findEntry(festivalId, application.entryId());
    if (entry.isLotteryEntry()) {
      //Wenn der Zieleintrag eine Lotterie ist, prüfen Sie, ob er gewonnen hat
      LotteryEntryResult entryResult = lotteryEntryResultRepository.findLotteryEntryResult(
          festivalId, memberId, entry.entryId());

      if (entryResult.lotteryResult() == LotteryResult.failed) {
        EntryId followingEntryId = ((LotteryEntry)entry).followingEntryId();
        if (followingEntryId == null) {
          throw new BusinessErrorException("Ich habe das Zielturnier nicht gewonnen");
        } else {
          LotteryEntryResult followingEntryResult =
              lotteryEntryResultRepository.findLotteryEntryResult(
                  festivalId, memberId, followingEntryId);

          if (followingEntryResult.lotteryResult() == LotteryResult.failed) {
            throw new BusinessErrorException("Ich habe das Zielturnier nicht gewonnen");
          }
        }
      }
    }

** Nach dem Refactoring **

PaymentCommandService.java


    Entry entry = entryRepository.findEntry(festivalId, application.entryId());
    if (entry.isLotteryEntry()) {
      //Wenn der Zieleintrag eine Lotterie ist, prüfen Sie, ob er gewonnen hat
      LotteryEntryResults lotteryEntryResults =
          lotteryEntryResultRepository.findLotteryEntryResults(
              festivalId, memberId, entry.entryId());

      if (!lotteryEntryResults.winning()) {
        throw new BusinessErrorException("Ich habe das Zielturnier nicht gewonnen");
      }
    }

Sauberer mit weniger if-Anweisungen: thumbsup:

Refactoring, wo Punkte verwendet werden

In Bezug auf die Verarbeitung der Punktnutzung

Ich denke, es ist gut, MemberPoints zu einer erstklassigen Sammlung zu machen, aber ich habe den Eindruck, dass das Innere von for lang ist. Ich dachte, dass es sauberer wäre, MemberPoint selbst, das ein Sammlungsmitglied ist, in den meisten memberPoints für zu haben.

Ich bin der Meinung, dass die Punktbalance eine Methode ist, die der Punktklasse gegeben werden sollte. Auch das Ablaufdatum ist mit plusYears (1) fest codiert, aber es scheint besser, dies auch in der Point-Klasse zu haben.

Ich erhielt Kommentare wie und versuchte, unter Bezugnahme auf die erhaltenen Kommentare umzugestalten.

** Vor dem Refactoring **

MemberPoints.java


public class MemberPoints {

  private List<MemberPoint> list;

  /**
   *Bestimmen Sie, ob der Punkt des Arguments verwendet werden kann, und verwenden Sie ihn ab dem Punkt nahe dem Ablaufdatum.
   *Ändern Sie den Status des MemberPoint-Objekts, das beibehalten werden soll.
   */
  public void usePoints(LocalDate paymentDate, PointAmount pointAmount) {

    //Verwenden Sie Punkte aus den bisher angegebenen Punkten, bis dieser Wert Null wird
    BigDecimal x = pointAmount.value();

    for (MemberPoint memberPoint : list) {

      //Ablaufdatum prüfen
      LocalDate expirationDate = memberPoint.givenPointDate().plusYears(1);
      if (paymentDate.compareTo(expirationDate) > 0) {
        continue;
      }

      //Punktausgleich prüfen
      BigDecimal availableUsePoint = memberPoint.givenPoint().value()
          .subtract(memberPoint.usedPoint().value());
      if (availableUsePoint.compareTo(BigDecimal.ZERO) == 0) {
        continue;
      }

      if (availableUsePoint.compareTo(x) <= 0) {
        memberPoint.use(availableUsePoint);
        x = x.subtract(availableUsePoint);
      } else {
        memberPoint.use(x);
        x = BigDecimal.ZERO;
        break;
      }
    }

    //Verwenden Sie Punkte von dem Punkt mit dem nächstgelegenen Ablaufdatum und Fehler, wenn die Anzahl der Punkte, die Sie verwenden möchten, nicht erreicht wird
    if (x.compareTo(BigDecimal.ZERO) > 0) {
      throw new BusinessErrorException("Unzureichende Punkte");
    }
  }
}

** Nach dem Refactoring **

MemberPoints.java


public class MemberPoints {

  private List<MemberPoint> list;

  /**
   *Bestimmen Sie, ob der Punkt des Arguments verwendet werden kann, und verwenden Sie ihn ab dem Punkt nahe dem Ablaufdatum.
   *Ändern Sie den Status des MemberPoint-Objekts, das beibehalten werden soll.
   */
  public void usePoints(LocalDate paymentDate, PointAmount usePointAmount) {

    PointAmount pointBalance = pointBalance(paymentDate);
    if (pointBalance.value().compareTo(usePointAmount.value()) < 0) {
      throw new BusinessErrorException("Unzureichende Punkte");
    }

    //Verwenden Sie Punkte aus den bisher angegebenen Punkten, bis dieser Wert Null wird
    BigDecimal x = usePointAmount.value();

    for (MemberPoint memberPoint : list) {

      //Ablaufdatum prüfen
      if (memberPoint.hasPassedExpirationDate(paymentDate)) {
        continue;
      }

      //Punktausgleich prüfen
      PointAmount availablePoint = memberPoint.availablePoint(paymentDate);
      if (!availablePoint.isPositive()) {
        continue;
      }

      if (availablePoint.value().compareTo(x) <= 0) {
        memberPoint.use(availablePoint);
        x = x.subtract(availablePoint.value());
      } else {
        memberPoint.use(new PointAmount(x));
        break;
      }
    }
  }

  private PointAmount pointBalance(LocalDate paymentDate) {

    PointAmount result = new PointAmount(BigDecimal.ZERO);

    for (MemberPoint memberPoint : list) {
      PointAmount availablePoint = memberPoint.availablePoint(paymentDate);
      result = result.add(availablePoint);
    }

    return result;
  }
}

MemberPoint.java


public class MemberPoint implements Entity {

  private MemberId memberId;
  private LocalDate givenPointDate;
  private PointAmount givenPoint;
  private PointAmount usedPoint;

  /**
   *Gibt die Anzahl der Punkte zurück, die am durch das Argument angegebenen Zieldatum verfügbar sind.
   */
  PointAmount availablePoint(LocalDate targetDate) {

    if (targetDate.compareTo(expirationDate()) > 0) {
      return new PointAmount(BigDecimal.ZERO);
    }

    BigDecimal result = givenPoint.value().subtract(usedPoint.value());
    return new PointAmount(result);
  }

  /**
   *Ablaufdatum zurückgeben.
   */
  LocalDate expirationDate() {
    return givenPointDate.plusYears(1);
  }

  /**
   *Gibt zurück, ob das Ablaufdatum abgelaufen ist.Gibt true zurück, wenn das Ablaufdatum abgelaufen ist.
   */
  boolean hasPassedExpirationDate(LocalDate paymentDate) {

    return paymentDate.compareTo(expirationDate()) > 0;
  }
}

Durch Erstellen einer Methode, die die Anzahl der für die Klasse "MemberPoint" verfügbaren Punkte zurückgibt, und einer Methode, die das Ablaufdatum zurückgibt und angibt, ob das Ablaufdatum abgelaufen ist, kann die Methode "usePoints" der Klasse "MemberPoints" etwas sauberer gestaltet werden. Es ist fertig.

schließlich

Es ist eher ein objektorientiertes Design als ein domänengesteuertes Design. Beides ist wirklich schwierig: teuer: Besonders in meinem Fall habe ich nur taktisches DDD, daher kenne ich den "begrenzten Kontext" überhaupt nicht ... Ich hoffe, dass ich eines Tages auch strategisches DDD ausgeben kann ...

Es gab auch einen anderen Endpunkt für die "Registrierung von Lotterieergebnissen", aber ich konnte mir keine gute Idee vorstellen, also gab ich diesmal auf. (Ich bin der Meinung, dass die Endpunktspezifikationen überhaupt nicht gut waren)

Dieses Mal habe ich versucht, ein Beispielprogramm mit dem Problem dieses Datenbankspezialisten zu erstellen, und ich habe das Gefühl, dass sich meine Erfahrung verbessert hat. Außerdem möchte ich die Probleme eines anderen Jahres herausfordern und eine Produktion produzieren, die etwas mehr als diesmal gewachsen ist.

Vielen Dank, dass Sie so weit gelesen haben. Kommentare von allen Ich bin sehr glücklich und habe viel zu lernen, daher würde ich mich freuen, wenn Sie Kommentare abgeben könnten.

Recommended Posts

Erstellen eines Beispielprogramms mit dem Problem eines Datenbankspezialisten für DDD-Verbesserung 2
Erstellen eines Beispielprogramms mit dem Problem eines Datenbankspezialisten mit DDD-Verbesserung 1
Ich habe versucht, ein Beispielprogramm mit dem Problem des Datenbankspezialisten für domänengesteuertes Design zu erstellen
Beispielprogramm, das den Hashwert einer Datei in Java zurückgibt
Verwenden der Datenbank (SQL Server 2014) aus einem Java-Programm 2018/01/04
Ein Programm, das die Anzahl der Wörter in einer Liste zählt
Ein Memorandum über das FizzBuzz-Problem
Reihenfolge der Verarbeitung im Programm
Erstellen eines Servlets in der Liberty-Umgebung
Ich habe ein Beispiel erstellt, wie ein Delegat in Swift UI 2.0 mit MapKit geschrieben wird
Messen Sie die Größe eines Ordners mit Java
Beispielcode zum Zuweisen eines Werts in der Eigenschaftendatei zu einem Feld des erwarteten Typs
Ich habe versucht, eine Datenbankverbindung in der Android-Entwicklung zu verwenden
Über das Problem des Deadlocks bei der Parallelverarbeitung in gem'sprockets '4.0
Ich habe ein Sequenzdiagramm des j.u.c.Flow-Beispiels geschrieben
[Ruby] Gewöhnen Sie sich an, beim Erstellen einer Kopie einer Zeichenfolgenvariablen die dup-Methode zu verwenden
Ein Programm (Java), das die Summe von ungeraden und geraden Zahlen in einem Array ausgibt
Beispielcode zum Abrufen der Werte der wichtigsten SQL-Typen in Java + Oracle Database 12c
Erstellen Sie eine Datenbank aller Bücher, die im letzten Jahrhundert in Japan im Umlauf waren
Ich habe versucht, ein Programm in Java zu erstellen, das das Problem des Handlungsreisenden mit einem genetischen Algorithmus löst
Vorlagenerstellungsprogramm bei Verwendung der Erinnerungsfunktion im Ruhezustand
Eine kurze Erklärung der fünf Arten von Java Static
Überprüfen Sie die Abhängigkeit eines bestimmten Maven-Artefakts in Coursier
Was ist die Darstellung von Domänenwissen im [DDD] -Modell?
Verfahren zum Veröffentlichen einer Anwendung mit AWS (4) Erstellen einer Datenbank
Erklärung von Ruby on Rails für Anfänger ③ ~ Erstellen einer Datenbank ~
Finden Sie mit Kotlin die Anzahl der Tage in einem Monat