[GO] Enveloppez et encapsulez la cible de validation pour éviter les complications

[Ajout] Quand j'y pense, ce n'était pas un décorateur parce que je n'ai pas joué avec les méthodes existantes. C'est le nom que le nom du motif ne sort pas soudainement. Est-ce juste une façade?

TL;DR

En implémentant séparément la classe wrapper pour Validation et la classe wrapper pour Creation, l'interface pour chaque responsabilité est spécifiée.

Quand est-ce efficace

Lorsque la logique de validation est requise séparément de la validation à l'aide de la fonction du cadre. Plus précisément, lors de la mise en œuvre d'un système tel qu'OpenID Connect qui nécessite qu'une réponse d'erreur soit renvoyée par redirection, ce qui ne peut pas être géré par la réponse d'erreur générée par le framework.

Pour plus de commodité, la logique qui génère la réponse d'erreur OpenID Connect est prise ici comme exemple, mais elle s'applique à tout système où une validation complexe doit être implémentée.

Qu'est-ce que tu veux faire

Lors de la conversion d'un paramètre de requête en objet de domaine, si la validation et la génération d'objet de domaine sont effectuées sur la base d'un objet de requête, la lisibilité du code se détériorera, je voudrais donc éviter cela.

Par exemple, dans OpenID Connect, la quantité de code dans le volume est assez importante simplement en validant la demande d'autorisation, et pour les performances, il est souvent souhaitable de conserver l'objet créé lors de la validation dans le contexte. Vous devez également prendre soin du modèle «en fait, c'était une requête OAuth2 au lieu d'OpenID Connect», donc le nombre de branches dans la logique de validation est ridicule.

Si possible, je voudrais générer une classe d'objet de requête pour chaque flux de branchement et réaliser une logique de validation branchée avec polymorphisme afin de ne pas provoquer d'explosion de cas de test.

Cependant, en réalité, il y a beaucoup de processus communs quel que soit le flux, et des méthodes comme request.isImplicitFlow () sont appelées de partout, elles seront donc implémentées dans l'objet request.

Les méthodes ci-dessus ne sont utilisées que lors de la validation. C'est parce que Flow est statique après avoir été converti en objet de domaine, et probablement une classe comme ʻImplicitFlowHandler` devrait faire le travail.

Pour rappel, il ne s'agit pas seulement d'OpenID Connect

Donc, si vous préparez une classe wrapper pour la validation et implémentez la méthode de délégation, la méthode ne sera pas implémentée dans l'objet de requête lui-même, donc la visibilité du code sera meilleure.

Exemple de code

Puisque la couche Controller reçoit les paramètres de requête HTTP, le champ est toujours «String».

package jp.hoge.openidconnect.authorization;
import lombok.Getter;

//Objet de la demande
// (Objet pour lier les paramètres de requête HTTP)
@Getter //Getter doit être défini
public class AuthorizationRequest {
  private String responseType;
  private String clientId;
  private String scope;
}
package jp.hoge.openidconnect.authorization.validation;

import jp.hoge.openidconnect.core.ResponseType;

//Classe wrapper pour la validation.Hériter de la classe d'emballage.
//La visibilité est toujours la valeur par défaut du package. 
class RequestWrapperForValidation extends AuthorizationRequest {

  private final AuthorizationRequest rawRequest;
  
  private ResponseType responseType = null;

  //La visibilité du constructeur est également un package
  RequestWrapperForValidation(final AuthorizationRequest rawRequest) {
    this.rawRequest = rawRequest;
  }

  //Une méthode que cela utilise uniquement dans la couche de validation.
  //Le véritable OpenID Connect est plus compliqué, mais c'est compliqué, c'est donc une image approximative
  boolean isImplicitFlow() {
    if (rawRequest.getScope().contains("openid")) {
      return _getResponseType().contains(ResponseType.ID_TOKEN);
    } else {
      return _getResponseType().contains(ResponseType.TOKEN);
    }
  }

  //Méthode de conversion en objet de domaine une seule fois
  //Vous pouvez faire référence à l'objet généré ici plusieurs fois dans le processus de validation..
  //Cependant, Thread-Expliquez clairement que ce n'est pas sûr.
  private ResponseType _getResponseType() {
    if (this.responseType == null) {
      this.responseType = ResponseType.parse(rawRequest.getResponseType());
    }
    return this.responseType;
  }

  //Méthode de délégation pour la classe d'emballage
  public String getResponseType() {
    return rawRequest.getResponseType();
  }
  // ...Après cela, nous mettrons en œuvre la méthode de délégation
}

Si vous l'implémentez comme ceci (que le code soit bon ou mauvais), vous pouvez utiliser la méthode de validation du même package (couche de validation), mais elle a la même apparence que l'objet d'origine d'autres couches.

Dans le cas de spécifications Web telles qu'OpenID Connect, peu importe si l'objet de requête se trouve dans la couche de domaine, mais pour une utilisation occasionnelle, il enveloppe l'interface de l'objet de requête au lieu de l'objet de requête. Sinon, la couche domaine fera référence à la classe de la couche contrôleur, donc c'est NG.

Comme pour la validation, la préparation d'une classe wrapper lors de la création aidera également à empêcher la couche anti-corruption de gonfler. Mais dans ce cas, il est préférable de l'implémenter avec juste un modèle Builder.

public class AuthorizationModelBuilder {
  private final AuthorizationRequest rawRequest;

  public AuthorizationModel toDomainModel() {
    // ...Processus de conversion en modèle de domaine
  }
}

C'était un échec de citer OpenID Connect, la connaissance du domaine était trop spéciale w

Notes complémentaires

Ce n'est pas seulement une question de validation. Pour les couches et les packages avec des responsabilités différentes, il est préférable d'utiliser des objets enveloppés pour clarifier l'interface et l'utilisation afin d'améliorer la lisibilité. Bien sûr, le coût de mise en œuvre est élevé, alors soyez conscient du compromis.

Recommended Posts

Enveloppez et encapsulez la cible de validation pour éviter les complications
Mémorandum Python: reportez-vous au texte et modifiez le nom du fichier lors de la copie du fichier cible
Conversion du système de coordonnées en ECEF et géodésique
Expérimentons et laissons des preuves pour déterminer les spécifications.