Recommandation de classe de service dans Ruby on Rails

Bonjour, c'est @ hairgai. Cette fois, j'écrirai ma propre utilisation des avantages et des inconvénients de la classe Service.

Quelle est la classe de service?

Groupe de classes décrivant une fonction dérivée (et auto-reconnue) d'un service dans DDD (Domain Driven Design). Je vais l'omettre car il y a beaucoup de gens qui l'expliquent en détail, mais je l'utilise souvent parce que je peux écrire la logique métier proprement entre le modèle et le contrôleur. Cette fois (bien que mon utilisation puisse être erronée), j'écrirai ma propre utilisation et la partie qui semble être son mérite.

Utilisation de base

Tout d'abord, je voudrais présenter l'utilisation de base avec un exemple de code. (Je ne sais pas si c'est la bonne réponse, mais je pense que ce n'est pas grave car c'est facile à voir.) Par exemple, si vous décrivez la fonction de «suivre» sur SNS, etc. en tant que couche de service dans un fichier, cela ressemblera à ceci. ** * Je répète, mais ce n'est pas la bonne réponse. ** **

class FollowService
  attr_reader :user, :target_user
  attr_accessor :follow

  def initialize(user, target_user)
    @user = user
    @target_user = target_user
  end

  def perform
    check!

    create_follow!
    run_after_worker!
  end

  private

  def check!
    check_following!
    check_blocking!
  end

  def check_following!
    return true unless user.following?(target_user)

    raise ArgumentError, 'User following target user'
  end

  def check_blocking!
    return true unless user.blocking?(target_user)

    raise ArgumentError, 'User blocking target user'
  end

  def create_follow!
    self.follow = user.follows.create!(target_user: target_user)
  end

  def run_after_worker!
    AfterFollowWorker.perform_in(0.2.seconds, follow.id)
  end
end

Créer avec des classes Ruby pures

En gros, je ne fais rien avec Gem etc. En l'implémentant en Ruby pur, nous visons l'effet "d'abaisser la barrière à la compréhension de l'implémentation". La classe de service a tendance à avoir une logique compliquée (quand il s'agit de fonctionnalités lourdes), j'essaie donc de la garder aussi simple que possible afin que tout le monde puisse la comprendre immédiatement.

Déterminez le nom de la classe

Je pense que vous aimez la dénomination, mais comme c'est une classe qui symbolise la fonction, elle est unifiée avec [verb]([objectif]) Service. Avoir une convention de dénomination a l'avantage de faciliter la déduction des fonctions.

Rendre les méthodes publiques uniquement

Je pense qu'il y a beaucoup de préférence ici (beaucoup de gens l'appellent "call"), mais fondamentalement, une seule méthode publique est utilisée, et les autres ne peuvent pas être appelées. Cela est dû au fait que la classe Service est une classe qui symbolise une seule fonction et que les fonctions qui peuvent être réalisées en l'utilisant sont limitées à une seule. Il y a pas mal de gens qui parlent de cette méthode publique unique, mais je l'ai aussi adoptée car il n'y avait aucune hésitation au moment de la conception et la vitesse de mise en œuvre a considérablement augmenté.

Seules les méthodes privées sont appelées par des méthodes publiques

Ceci est également adopté car il améliore la visibilité + 1 La responsabilité de chaque méthode devient plus légère et la clause de garde devient plus facile à utiliser. Je pense que c'est là que vous avez des goûts différents.

Approche unique du post-traitement, etc.

Comme je l'ai écrit dans Follow Service ci-dessus, je pense d'abord à la réponse à l'utilisateur tant qu'elle est utilisée dans les affaires. En tant qu'approche dans un tel cas, il existe une méthode pour "effectuer uniquement le traitement nécessaire pendant une réponse", et le traitement ultérieur, etc., est stocké dans la file d'attente en tant que travail et traité par le serveur de travaux, etc. À ce moment-là, si le processus d'appel du travail est dispersé à divers endroits tels que le contrôleur et le modèle, la visibilité de l'ensemble du projet sera mauvaise.

Par conséquent, vous pouvez améliorer les perspectives générales en utilisant une convention de dénomination telle que ʻAfterHogeJob pour les travaux de post-traitement de HogeService` et en appelant uniquement dans la classe Service.

Élimination du ballonnement du contrôleur

Il va sans dire.

class FollowsController < ApplicationController
  def create
    target_user = user.find(params[:target_user_id])
    service = FollowService.new(current_user, target_user)
    service.perform

    redirect_to user_path(target_user)
  end
end

Élimination du gonflement du modèle

Il va sans dire. Apportez tout ce qui est écrit dans le modèle à la classe Service, et le modèle ne sera que l'association de données, la validation et d'autres méthodes liées à un seul modèle. En conséquence, il n'y a plus de méthodes riches en logique dans le modèle, ni de modèles gonflés et difficiles à voir.

Épilogue

Quand je l'écris à nouveau de cette manière, je pense que j'écris d'une manière qui met diverses responsabilités sur la classe Service. Je pense que l'on peut soutenir que la classe Service n'est pas nécessaire, mais comme une telle histoire de conception n'est qu'une des méthodes, ce serait bien si elle pouvait être utilisée de manière appropriée en fonction de notre entreprise.

Recommended Posts

Recommandation de classe de service dans Ruby on Rails
Ruby on Rails compatible japonais-anglais i18n
[Ruby on Rails] Introduction des données initiales
[Rails] Ajout de la fonction de commentaire Ruby On Rails
Ruby on Rails dans les espaces de codes Visual Studio
[Ruby on Rails] Notation japonaise de l'erreur
Explication de Ruby sur rails pour les débutants ①
[Ruby on rails] Implémentation d'une fonction similaire
Débutant a créé un portfolio avec Ruby on Rails
Effacer N + 1 dans les actes_as_tree de l'arborescence Ruby on Rails Gem
Implémentation de la fonction de connexion Ruby on Rails (Session)
[Ruby on Rails] Jusqu'à l'introduction de RSpec
Rails nouveau dans Ruby on Rails ~ Mémorandum jusqu'au déploiement 2
Ruby on Rails ~ Principes de base de MVC et du routeur ~
Ruby on Rails élémentaire
[Ruby on Rails] Un mémorandum de modèles de mise en page
Principes de base de Ruby on Rails
Rails nouveau dans Ruby on Rails ~ Mémorandum jusqu'au déploiement 1
(Ruby on Rails6) Création de données dans une table
Association Ruby On Rails
Une série de flux de création de table → création, suppression d'enregistrement → suppression de table dans Ruby on Rails
[Ruby on Rails] Affichage individuel des messages d'erreur
[Ruby on Rails] Communication asynchrone de la fonction de publication, ajax
[Ruby on Rails] Comment écrire enum en japonais
Implémentation de la fonction de connexion Ruby on Rails (édition de devise)
[Ruby On Rails] Comment réinitialiser DB dans Heroku
[Ruby / Rails] Définissez une valeur unique (unique) dans la classe
[Ruby on Rails] Fonction de prévisualisation de l'image dans le fichier
Explication de Ruby sur rails pour les débutants ⑥ ~ Création de validation ~
Explication de Ruby on rails pour les débutants ② ~ Création de liens ~
Organiser les publications par ordre de likes sur Rails (classement)
Essayez d'utiliser l'attribut de requête Ruby on Rails
Explication de Ruby on rails pour les débutants ⑦ ~ Implémentation Flash ~
Record d'apprentissage de Ruby on rails -2020.10.03
Record d'apprentissage Ruby on rails -2020.10.04
Écrire l'héritage de classe dans Ruby
[Ruby on Rails] Debuck (binding.pry)
Record d'apprentissage de Ruby on rails -2020.10.05
Record d'apprentissage de Ruby on rails -2020.10.09
Record d'apprentissage Ruby on rails-2020.10.07 ②
Record d'apprentissage Ruby on rails-2020.10.07 ①
Annuler la migration de Ruby on Rails
Record d'apprentissage de Ruby on rails -2020.10.06
Résumé de la validation Ruby on Rails
Mémorandum de base Ruby on Rails
Difficultés à créer un environnement Ruby on Rails (Windows 10) (SQLite3)