[RUBY] Trouvez une valeur pratique pour avoir une méthode et en faire un ValueObject

Lorsque je recherche une conception axée sur le domaine, je vois parfois des histoires de création de ValueObjects dans toutes les colonnes d'une base de données, ou de création de ValueObjects qui n'ont pas de méthodes simplement en entrant des valeurs. Je pense que c'est le résultat d'une trop grande conscience de la définition de ValueObject comme «immuable, échangeable et équivalent en tant que valeur». Mettant de côté une telle définition, je voudrais dire dans cette entrée que l'idée de "N'y a-t-il pas une valeur pratique s'il y a une méthode?" Peut trouver le ValueObject qui est vraiment nécessaire.

Modèle d'objet de valeur

Avant d'entrer dans le sujet principal, il est plus facile de trouver ValueObject si vous savez quelles méthodes sont utiles, alors parlons-en. Je trouve quatre modèles d'objets de valeur.

ValueObject du wrapper

Créez une méthode de jugement en enveloppant la valeur de code, etc.

Exemple de code [^ 1] [^ 2] avec le code d'état HTTP comme sujet et objet de valeur.

[^ 1]: Puisqu'il s'agit d'un exemple de code, il s'agit d'un jugement de code d'état approximatif.

[^ 2]: J'utilise attr_accessor, mais si vous craignez que l'immuabilité soit altérée, vous pouvez la rendre privée ou la changer pour faire référence à des variables d'instance.

http_status_code_value_object.rb


class HttpStatusCodeValueObject
  attr_accessor :status_code
  def initialize(status_code)
    self.status_code = status_code
  end

  #2xx Code d'état de réussite de réussite
  def success?
    200 <= status_code && status_code < 300
  end
end

Je pense que Ruby et Rails le font souvent en étendant le type standard. Vous pouvez écrire valeur% 2 == 1 comme value.odd?.

ValueObject calculable

En supposant que vous créez un site EC, la date de livraison estimée est calculée comme suit: «date de commande + temps de travail à expédier + délai de livraison de l'entrepôt au client». Cependant, il existe une plage de «temps de travail pour expédier» et de «délai de livraison de l'entrepôt au client», de sorte que le code ressemble à ceci.

order_date = Date.parse('2020-01-01')
work_days = 0..1 #Le temps de travail avant l'expédition est dans 1 jour
delivery_days = 1..2 #Le délai de livraison de l'entrepôt au client est de 1 à 2 jours
#Je suis heureux d'écrire ceci, mais j'obtiens une erreur
#Date de commande+Temps de travail pour expédier+Délai de livraison de l'entrepôt au client
# order_date + work_days       + delivery_days

#Réalité sévère
delivery_date_from = order_date
delivery_date_to = order_date
delivery_date_from += work_days.first
delivery_date_to += work_days.last
delivery_date_from += delivery_days.first
delivery_date_to += delivery_days.last

Alors, créez une classe en tant que ValueObject qui peut calculer la plage de dates.

date_range_value_object.rb


class DateRangeValueObject
  attr_accessor :from, :to
  def initialize(from, to = nil)
    self.from = from
    self.to = to || from
  end

  #Seul l'addition est définie car aucun autre opérateur n'est requis
  def +(other)
    other_from = other
    other_to = other
    if other.is_a?(Range)
      other_from = other.first
      other_to = other.last
    end
    self.class.new(from + other_from, to + other_to)
  end

  #Exporter vers des valeurs primitives
  def to_range
    from..to
  end
end

Cette classe facilite l'écriture des calculs.

value_object = DateRangeValueObject.new(order_date)
value_object += work_days
value_object += delivery_days
value_object.to_range

Je pense que c'est aussi quelque chose que Ruby et Rails font souvent en étendant les types standard.

ValueObject sensible et ValueObject insensible

Il est courant de vérifier la valeur et de lever une exception si une valeur étrange entre. Parfois, vous pouvez renvoyer le résultat d'un échec, sinon jusqu'à ce que vous lanciez une exception.

La personne qui lève une exception est comme lorsqu'une devise qui n'existe pas est spécifiée dans le calcul de l'argent, et si vous faites un modèle Money [^ 3], je pense que vous ferez une exception si c'est une devise invalide dans le constructeur. J'appelle ces objets de valeur des objets de valeur sensibles. [^ 3]: Un modèle célèbre comme objet de valeur pour l'argent Je pense que les objets de valeur qui apparaissent dans «Introduction à la conception pilotée par domaine» de M. Masanobu Naruse sont essentiellement des objets de valeur sensibles. [^ 4]

[^ 4]: Je ne l'ai pas lu correctement, donc je suis désolé si c'est différent ...

Le cas où vous pouvez renvoyer le résultat d'un échec est celui où le ValueObject de l'encapsuleur spécifie une valeur inattendue. J'ai écrit la classe HttpStatusCodeValueObject dans l'exemple de code du wrapper ValueObject, mais cela fonctionne bien même si -1 est spécifié pour status_code. Ceci est similaire à l'opération pour "NaN" dans le calcul en virgule flottante minoritaire et "NULL" dans RDB. Vous pouvez également l'utiliser pour la validation des entrées utilisateur en créant une méthode valide? Qui vérifie si une valeur valide est spécifiée. J'appelle ces objets de valeur des objets de valeur insensibles. Il est sensible car il réagit dès qu'une valeur invalide entre, et il est insensible car il n'est pas remarqué tant que «valide?» Est appelé.

Pensez "N'y a-t-il pas une valeur pratique s'il y a une méthode?"

Une technique pour trouver ce dont vous avez besoin est de tout mettre sur votre bureau, puis de retirer ce dont vous n'avez pas besoin. C'est un moyen facile à comprendre de terminer sans rien manquer. Vous pouvez également utiliser cette méthode lors de la recherche d'un ValueObject.

Tout d'abord, regardez la user story et l'écran sur lesquels vous travaillez, et notez les données utilisées dans le Bloc-notes. À partir de là, nous supprimerons les valeurs qui ne sont que des entrées et des sorties.

Même si le nom d'utilisateur est requis, il y a des cas où il est préférable de le laisser au processus de validation du framework sans créer un ValueObject sensible. [Varidata personnalisées](https://railsguides.jp/active_record_validations.html#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%] même dans des cas complexes tels que les numéros de téléphone A0% E3% 83% 90% E3% 83% AA% E3% 83% 87% E3% 83% BC% E3% 82% B7% E3% 83% A7% E3% 83% B3% E3% 82% 92% E5% AE% 9F% E8% A1% 8C% E3% 81% 99% E3% 82% 8B) peuvent être utilisés.

Posez une question sur les valeurs restantes.

[^ 5]: pas une seule valeur, mais plusieurs valeurs peuvent être utilisées comme un ensemble

Si oui dans les deux cas, créez un ValueObject. En plus de cela, je pense que la propriété en tant qu'objet de valeur peut être créée si nécessaire.

Remarque: pour les langues à typage statique, notation hongroise ([Application en hongrois](https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%B3%E3%82%AC%E3%83] % AA% E3% 82% A2% E3% 83% B3% E8% A8% 98% E6% B3% 95 #% E3% 82% A2% E3% 83% 97% E3% 83% AA% E3% 82% B1% E3% 83% BC% E3% 82% B7% E3% 83% A7% E3% 83% B3% E3% 83% 8F% E3% 83% B3% E3% 82% AC% E3% 83% AA% Je pense qu'il est normal de créer un ValueObject sans méthode afin de réaliser E3% 82% A2% E3% 83% B3)) avec un type.


Cette entrée concerne le dictionnaire de données (https://linyclar.github.io/software_development/requirements_analysis_driven_desgin/#%E3%83/) dans la conception axée sur l'analyse des exigences (https://linyclar.github.io/software_development/requirements_analysis_driven_desgin/) % 87% E3% 83% BC% E3% 82% BF% E3% 83% 87% E3% 82% A3% E3% 82% AF% E3% 82% B7% E3% 83% A7% E3% 83% 8A % E3% 83% AA) a été réorganisé et réécrit en contenu général. C'est une longue histoire, mais c'est une histoire de choses de type DDD dans Rails.

Recommended Posts

Trouvez une valeur pratique pour avoir une méthode et en faire un ValueObject
Comment créer une méthode pratique qui utilise des génériques et une interface fonctionnelle
Si vous souhaitez transformer une application Java en une image Docker, il est pratique d'utiliser jib.
Comment identifier le chemin sur lequel il est facile de se tromper
Une histoire qui a rendu pratique avec Kotlin qu'il est difficile d'exécuter une animation en continu sur Android
Comment tester une méthode privée et la simuler partiellement en Java
Script Shell qui construit une image Docker et la pousse vers ECR
Réglage des performances JVM: qu'est-ce que le réglage et comment élaborer un bon plan
Une méthode simple et pratique pour HashMap
Comment convertir une valeur d'un type différent et l'affecter à une autre variable
Code difficile à déboguer et à analyser
Une fonction qui ne s'affiche qu'une seule fois pendant l'exécution d'Alert et ne l'affiche plus jamais
Comment créer un plugin Vagrant que vous avez appris lorsque vous avez forké et publié vagrant-mutagen
Passez un argument à la méthode et recevez le résultat de l'opération comme valeur de retour
Une solution qui facilite la saisie des avantages et des tests Web pour vérifier les résultats
[Docker] Est-il suffisant de l'appeler une construction en plusieurs étapes? → L'histoire qui est devenue si bonne
Session était un cookie conçu pour être effacé lorsque le navigateur était fermé et était une méthode pour l'échanger: Tutoriel Rails Note - Qu'est-ce qu'une session Rails?
A vous qui regrettez que la méthode principale de Java soit statique
Attribuer des expressions lambda Java8 aux variables et les réutiliser
Rendre Docker déroutant avec Pokemon et le rendre plus facile à attacher
Je veux faire une liste avec kotlin et java!
Je veux appeler une méthode et compter le nombre
Je veux créer une fonction avec kotlin et java!
Rechercher une instruction Switch qui peut être convertie en une expression Switch
Je suis heureux d'avoir une méthode pour faire clignoter les caractères sur Android
Créez une application Web Spring Boot qui utilise IBM Cloudant et déployez-la sur Cloud Foundry