[JAVA] Ce que j'ai pensé en passant la valeur d'entrée de l'utilisateur à la classe Service

Ce que j'ai pensé en passant la valeur d'entrée de l'utilisateur à la classe Service

Conclusion

La conversion entre la classe Form qui reçoit la valeur d'entrée et l'entité est difficile, j'ai donc pensé à quatre solutions.

1.Faites de chaque valeur d'entrée un argument de la méthode Service 2. Créez des objets en unités significatives 3. Transformez la méthode Service en une classe Form 4. Faites de l'argument de service une interface

Je pense que les solutions qui pourraient être utilisées sont 2 et 4. Cette fois, nous l'avons résolu en 4 sur la base de la culture interne.

environnement

Hypothèse: la conversion entre la classe Form qui reçoit la valeur d'entrée et l'entité est difficile

Je convertissais avec Entity pour la persistance dans la classe Form comme suit.

PurchaseForm.java


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

  /**
   *Générer l'entité en fonction de la valeur d'entrée
   */
  public Purchase toEntity() {
    //réduction
  }
}

S'il s'agit d'une simple conversion, il n'y a aucun problème, mais si c'est le modèle suivant, il mourra.

Lorsque la classe à convertir a une structure de classe complexe

À mesure que la quantité de code dans le programme à convertir en Entité augmente, la visibilité du code source se détériore. Surtout lorsque vous essayez de générer une classe enfant de la classe à générer, c'est assez douloureux. Dans les projets dans lesquels je suis impliqué, je compte souvent sur AUTO INCREMENT pour la numérotation de la clé primaire, il est donc inévitablement nécessaire de compléter la clé primaire et la clé externe avec la classe Service. Une préoccupation est divisée en plusieurs classes, telles que l'initialisation d'une pièce avec Form et l'initialisation du reste avec la classe Service, et le code source devient difficile à suivre.

PurchaseForm.java


@Data
public class PurchaseForm {
  //réduction
  /**
   *Générer l'entité en fonction de la valeur d'entrée
   */
  public Purchase toEntity() {
    //Cette
    //Cette
    //Mais
    //Moi
    //Tsu
    //Chi
    //Ya
    //Longue
    //je
    //Quand
    //Conclusion
    //Structure
    //Un
    //Et al.
    //je
  }
}

C'est douloureux s'il y a beaucoup d'arguments requis pour la génération d'entité

Si vous avez besoin d'un objet persistant dans l'argument jusqu'à présent, vous ne devez plus le convertir dans la classe Form.

PurchaseForm.java


@Data
public class PurchaseForm {
  //réduction
  /**
   *Générer l'entité en fonction de la valeur d'entrée
   *Si vous prenez un grand nombre d'objets persistants comme arguments, vous vous demandez si vous devez créer une entité ici.
   */
  public Purchase toEntity(Hoge hoge, Fuga Futa, ... etc) {
    //réduction
  }
}

Solutions possibles

1.Faites de chaque valeur d'entrée un argument de la méthode Service

J'ai essayé mais c'est douloureux. Car à mesure que la valeur d'entrée de Form augmente, le nombre d'arguments augmente.

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. Créez des objets en unités significatives

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),...);
  }
}

L'avantage est que vous pouvez préconditionner l'objet argument pour garantir l'intégrité de la valeur. Les conditions préalables sont vérifiées dans le constructeur.

3. Transformez la méthode Service en une classe Form

C'est très simple, mais c'est un très mauvais modèle! Il est difficile pour la direction de dépendance d'être Form ← Service. Le formulaire est étroitement associé aux spécifications de l'écran, je ne veux donc pas que la logique métier dépende du formulaire. Il est étrange que la classe Service doive être modifiée car les spécifications de l'écran ont changé même si la logique métier n'a pas changé.

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);
  }
}

Cela a été résolu avec le quatrième plan.

4. Faites de l'argument de service une interface

Tout d'abord, j'ai défini l'interface de service et d'argument dans un package proche du domaine.

PurchaseService.java


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

PurchaseRequest.java


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

Ensuite, j'ai écrit la validation de valeur dans une autre classe d'implémentation.

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;
  }
}

L'intérêt de cette méthode d'implémentation est qu'elle déclare la valeur utilisée dans la classe Service au niveau de la couche proche du domaine, elle résiste donc aux changements côté écran. Bien sûr, si les entrées et les sorties changent en tant que concept commercial, elles doivent être revues, mais certaines différences peuvent être contrôlées côté écran.

Supplément

Mieux encore, je pense qu'il est bon de lier la valeur d'entrée de l'écran au type défini côté domaine. Je pense que c'est souvent considéré comme la solution optimale. Ce qui précède est la solution que nous avons apportée dans notre projet en l'absence d'une culture d'implémentation de la validation sur des entités côté domaine, ou de capture et d'implémentation sémantique de types valeur.

Effet secondaire inattendu

Après cela, j'ai décidé de créer une API pour calculer le prix d'achat en fonction des informations sur les produits achetés et la quantité. Si vous définissez une méthode appelée PurchaseService # CalculatePrice (CalculatePriceRequest) et laissez PurchaseRequest hériter deCalculatePriceRequest, vous pouvez facilement calculer le prix d'achat même avec un argument de type PurchaseRequest. C'est vrai, puisqu'il y a des informations sur le produit et la quantité dans la demande d'achat de produit, il est tout à fait naturel de pouvoir calculer le prix d'achat sur la base de ces informations.

finalement

Je fais beaucoup d'essais et d'erreurs, mais je pense que je devrais être en mesure d'augmenter la main de la méthode de montage en analysant les modèles qui ont fonctionné ou qui n'ont pas fonctionné. En particulier, je développe souvent des applications WEB, j'aimerais donc réfléchir à différentes IO avec l'écran.

Recommended Posts

Ce que j'ai pensé en passant la valeur d'entrée de l'utilisateur à la classe Service
Ce à quoi j'étais accro lors de l'introduction de la bibliothèque JNI
Ce à quoi j'ai pensé lorsque j'ai commencé à migrer de Java vers Kotlin
Mémorandum: Ce à quoi j'étais accro quand j'ai frappé l'API de comptabilité freee
Enregistré parce que j'étais accro à l'entrée standard de la classe Scanner
Ce que j'ai essayé quand je voulais obtenir tous les champs d'un haricot
Que faire lorsque la valeur devient nulle dans le second getSubmittedValue () dans JSF Validator
Faites attention à la vérification des limites de la valeur d'entrée lors de l'utilisation du type float
Ce que j'ai fait lorsque j'ai converti Java en Kotlin
Je veux obtenir la valeur en Ruby
Que faire lorsque le certificat SSL a expiré
Ce que j'ai pensé quand j'ai commencé à travailler comme ingénieur
Que dois-je faire pour recharger le Dockerfile mis à jour?
Ce que j'ai corrigé lors de la mise à jour vers Spring Boot 1.5.12 ・ Ce à quoi j'étais accro
Ce à quoi j'étais accro avec l'API REST Redmine
Je veux donner un nom de classe à l'attribut select
Je souhaite rechercher de manière récursive la liste des classes sous le package
Je souhaite renvoyer plusieurs valeurs de retour pour l'argument saisi
L'histoire à laquelle j'étais accro lors de la création de STS
Je veux juger de la nécessité de tester en comparant les différences des fichiers de classe lors de la refactorisation de Java.
Je veux rendre le cadre de la zone de texte rouge lorsqu'il y a une erreur de saisie
Lorsque j'ai essayé d'exécuter mon propre service, il a échoué, alors je l'ai vissé dans le planificateur de tâches
9 Correspond à la valeur de retour
Qu'est-ce que la classe BufferedReader?
Entrée dans la console Java
[java] Ce que j'ai fait en comparant des listes dans ma propre classe
J'ai essayé de résumer ce qui était demandé lors de l'édition site-java-
Que faire lorsque les modifications du servlet ne sont pas reflétées
Comment afficher 0 sur le côté gauche de la valeur d'entrée standard
Comment afficher la valeur lorsqu'il y a un tableau dans le tableau
Ce que j'ai fait lorsque la base de données n'a pas démarré avec docker-compose up
J'ai essayé de traduire le message d'erreur lors de l'exécution d'Eclipse (Java)
Ce que j'ai fait lors de la migration de la série Spring Boot 1.4 vers la série 2.0
Un moyen simple de créer une classe de mappage lors de l'utilisation de l'API
Ce à quoi j'étais accro lors de la mise en œuvre de l'authentification Google avec des rails
Je veux limiter l'entrée en réduisant la plage de nombres
Ce que j'ai fait lors de la migration de la série Spring Boot 1.5 vers la série 2.0
Je veux changer la valeur de l'attribut dans Selenium of Ruby