[JAVA] Création d'un exemple de programme en utilisant le problème d'un spécialiste des bases de données dans DDD Improvement 2

introduction

Comme la dernière fois, il s'agit d'un article remanié sur la création d'un exemple de programme avec DDD en utilisant le problème des spécialistes des bases de données.

J'ai créé un exemple de programme en utilisant le problème du spécialiste des bases de données dans la conception pilotée par domaine Création d'un exemple de programme en utilisant le problème d'un spécialiste des bases de données avec DDD, amélioration 1

Cette fois, j'ai essayé de refactoriser la partie liée au point de terminaison "Dépôt".

Le code source est ici La version au moment de la publication de cet article est la balise 1.2.

Refactoriser le jugement gagnant de la loterie par une loterie en plusieurs étapes

Dans le premier article publié

Dans une loterie en plusieurs étapes, juger le gain de la loterie de l'extérieur de l'entrée fait fuir la logique vers l'Application, et surtout, vous le regretterez plus tard lorsque le nombre d'étapes augmentera. Il ne dit pas qu'il y a deux étapes, et si vous l'augmentez à deux étapes, il vous sera demandé de l'augmenter immédiatement d'une étape supplémentaire.

Je l'ai généralement commenté. J'ai essayé ça.

Tout d'abord, obtenons le résultat de la loterie de l'application de participation à la loterie, y compris le résultat de la loterie en plusieurs étapes, enregistrez-le dans la collection de première classe et permettez-nous de savoir si vous avez gagné ou non avec la collection de première classe. C'était.

LotteryEntryResults.java


public class LotteryEntryResults {

  private List<LotteryEntryResult> list;

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

  /**
   *Renvoie si vous avez gagné.Renvoie vrai en cas de gain.
   */
  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);
  }

On m'a demandé d'essayer d'utiliser le traitement récursif, mais je n'avais jamais fait de traitement récursif avec SQL, alors cette fois j'ai essayé boueux: sweat_smile:

Cela a permis de déplacer le jugement gagnant de la loterie de la couche application à la couche domaine dans une loterie en plusieurs étapes. De plus, il est désormais possible de gérer une loterie en plusieurs étapes avec 3 étapes ou plus.

** Avant refactoring **

PaymentCommandService.java



    Entry entry = entryRepository.findEntry(festivalId, application.entryId());
    if (entry.isLotteryEntry()) {
      //Si l'entrée cible est une loterie, vérifiez si elle a gagné
      LotteryEntryResult entryResult = lotteryEntryResultRepository.findLotteryEntryResult(
          festivalId, memberId, entry.entryId());

      if (entryResult.lotteryResult() == LotteryResult.failed) {
        EntryId followingEntryId = ((LotteryEntry)entry).followingEntryId();
        if (followingEntryId == null) {
          throw new BusinessErrorException("Je n'ai pas gagné le tournoi cible");
        } else {
          LotteryEntryResult followingEntryResult =
              lotteryEntryResultRepository.findLotteryEntryResult(
                  festivalId, memberId, followingEntryId);

          if (followingEntryResult.lotteryResult() == LotteryResult.failed) {
            throw new BusinessErrorException("Je n'ai pas gagné le tournoi cible");
          }
        }
      }
    }

** Après refactoring **

PaymentCommandService.java


    Entry entry = entryRepository.findEntry(festivalId, application.entryId());
    if (entry.isLotteryEntry()) {
      //Si l'entrée cible est une loterie, vérifiez si elle a gagné
      LotteryEntryResults lotteryEntryResults =
          lotteryEntryResultRepository.findLotteryEntryResults(
              festivalId, memberId, entry.entryId());

      if (!lotteryEntryResults.winning()) {
        throw new BusinessErrorException("Je n'ai pas gagné le tournoi cible");
      }
    }

Nettoyeur avec moins d'instructions if: thumbsup:

Refactoring où les points sont utilisés

Concernant le traitement de l'utilisation des points

Je pense que c'est bien de faire des MemberPoints une collection de première classe, mais j'ai l'impression que l'intérieur de for est long. J'ai pensé qu'il serait plus propre d'avoir MemberPoint lui-même, qui est un membre de la collection, dans la plupart des membres pour.

Je pense que la balance des points est une méthode qui devrait être donnée à la classe Point. De plus, la date d'expiration est codée en dur avec plusYears (1), mais il semble préférable de l'avoir également dans la classe Point.

J'ai reçu des commentaires tels que, et j'ai essayé de refactoriser en référence aux commentaires reçus.

** Avant refactoring **

MemberPoints.java


public class MemberPoints {

  private List<MemberPoint> list;

  /**
   *Déterminer si le point de l'argument peut être utilisé et l'utiliser à partir du point proche de la date d'expiration.
   *Modifiez l'état de l'objet MemberPoint à conserver.
   */
  public void usePoints(LocalDate paymentDate, PointAmount pointAmount) {

    //Utilisez les points des points donnés jusqu'à présent jusqu'à ce que cette valeur devienne zéro
    BigDecimal x = pointAmount.value();

    for (MemberPoint memberPoint : list) {

      //Vérification de la date d'expiration
      LocalDate expirationDate = memberPoint.givenPointDate().plusYears(1);
      if (paymentDate.compareTo(expirationDate) > 0) {
        continue;
      }

      //Vérifier le solde des points
      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;
      }
    }

    //Utilisez des points de celui avec la date d'expiration la plus proche, et une erreur si le nombre de points que vous souhaitez utiliser n'est pas atteint
    if (x.compareTo(BigDecimal.ZERO) > 0) {
      throw new BusinessErrorException("Points insuffisants");
    }
  }
}

** Après refactoring **

MemberPoints.java


public class MemberPoints {

  private List<MemberPoint> list;

  /**
   *Déterminer si le point de l'argument peut être utilisé et l'utiliser à partir du point proche de la date d'expiration.
   *Modifiez l'état de l'objet MemberPoint à conserver.
   */
  public void usePoints(LocalDate paymentDate, PointAmount usePointAmount) {

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

    //Utilisez les points des points donnés jusqu'à présent jusqu'à ce que cette valeur devienne zéro
    BigDecimal x = usePointAmount.value();

    for (MemberPoint memberPoint : list) {

      //Vérification de la date d'expiration
      if (memberPoint.hasPassedExpirationDate(paymentDate)) {
        continue;
      }

      //Vérifier le solde des points
      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;

  /**
   *Renvoie le nombre de points disponibles à la date cible spécifiée par l'argument.
   */
  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);
  }

  /**
   *Date d'expiration du retour.
   */
  LocalDate expirationDate() {
    return givenPointDate.plusYears(1);
  }

  /**
   *Renvoie si la date d'expiration est dépassée.Renvoie true si la date d'expiration est dépassée.
   */
  boolean hasPassedExpirationDate(LocalDate paymentDate) {

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

En créant une méthode qui retourne le nombre de points qui peuvent être utilisés dans la classe MemberPoint et une méthode qui renvoie la date d'expiration et si la date d'expiration est passée, il est possible de rendre la méthode ʻusePoints de la classe MemberPoints` plus propre. C'est fait.

à la fin

Cela ressemble plus à une conception orientée objet qu'à une conception axée sur le domaine. Les deux sont vraiment difficiles: chers: Surtout dans mon cas, je n'ai que du DDD tactique, donc je ne connais pas du tout le "contexte borné" ... J'espère qu'un jour je pourrai également produire un DDD stratégique ...

De plus, il y avait un autre point final de "l'enregistrement des résultats de loterie", mais je ne pouvais pas penser à une bonne idée, alors j'ai abandonné cette fois. (Je pense que les spécifications du point final n'étaient pas bonnes en premier lieu)

Cette fois, j'ai essayé de créer un exemple de programme en utilisant le problème de ce spécialiste des bases de données, et je pense que mon expérience s'est améliorée. Aussi, je voudrais contester en utilisant les problèmes d'une autre année et produire une production qui a augmenté un peu plus que cette fois.

Merci d'avoir lu jusqu'ici. Commentaires de tout le monde Je suis très heureux et j'ai beaucoup à apprendre, alors je vous serais reconnaissant si vous pouviez commenter.

Recommended Posts

Création d'un exemple de programme en utilisant le problème d'un spécialiste des bases de données dans DDD Improvement 2
Création d'un exemple de programme en utilisant le problème d'un spécialiste des bases de données avec DDD Improvement 1
J'ai essayé de créer un exemple de programme en utilisant le problème du spécialiste des bases de données dans la conception pilotée par domaine
Exemple de programme qui renvoie la valeur de hachage d'un fichier en Java
Utilisation de la base de données (SQL Server 2014) à partir d'un programme Java 04/01/2018
Un programme qui compte le nombre de mots dans une liste
Un mémorandum du problème FizzBuzz
Ordre de traitement dans le programme
Création d'un servlet dans l'environnement Liberty
J'ai fait un exemple de la façon d'écrire un délégué dans Swift UI 2.0 à l'aide de MapKit
Mesurer la taille d'un dossier avec Java
Exemple de code pour attribuer une valeur dans le fichier de propriétés à un champ du type attendu
J'ai essayé d'utiliser une connexion à une base de données dans le développement Android
À propos du problème de blocage dans le traitement parallèle dans la version 4.0 de gem'sprockets
J'ai écrit un diagramme de séquence de l'exemple j.u.c.Flow
[Ruby] Prenez l'habitude d'utiliser la méthode dup lors de la copie d'une variable chaîne
Un programme (Java) qui génère la somme des nombres pairs et impairs dans un tableau
Exemple de code pour obtenir les valeurs des principaux types SQL dans Java + Oracle Database 12c
Créer une base de données de tous les livres qui ont circulé au Japon au siècle dernier
J'ai essayé de créer un programme en Java qui résout le problème du voyageur de commerce avec un algorithme génétique
Programme de création de modèle lors de l'utilisation de la fonction de rappel dans Slack
Une explication rapide des cinq types de statique Java
Vérifiez la dépendance d'un artefact maven spécifique dans Coursier
Quelle est la représentation de la connaissance du domaine dans le modèle [DDD]?
Procédure de publication d'une application à l'aide d'AWS (4) Création d'une base de données
Explication de Ruby on rails pour les débutants ③ ~ Création d'une base de données ~
Trouvez le nombre de jours dans un mois avec Kotlin