[RUBY] Une histoire qui sépare la logique métier et le modèle

Aperçu

C'est une histoire qui coupe l'accès à une ressource de données spécifique de la logique métier et du traitement du traitement dans le service qui est exploité et la standardise.

Est-ce vraiment un modèle?

Je ne comprends pas. L'entité ou l'objet de domaine est-il meilleur? S'il vous plaît, faites-moi savoir.

Avant la modélisation

Par exemple, c'est le code. Voici une classe qui gère la page d'une préfecture, et il est supposé que vous avez acquis un événement associé à cette préfecture.

area/pref.rb


module Area
  class Pref
    def event
      events = Api.call('/events', params)
      events.each_with_object([]) do |event, memo|
        memo << {
          name: event[:name],
          address: create_full_address(event)
        }
      end
    end

    def create_full_address(event)
      #Renvoie la chaîne complète d'adresses en combinant certaines informations
    end
  end
end

Ensuite, la page des villes, quartiers, villes et villages sous la préfecture a été créée. Vous trouverez ci-dessous les événements liés à cette ville.

area/city.rb


module Area
  class City
    def event
      events = Api.call('/events', params)
      events.each_with_object([]) do |event, memo|
        memo << {
          name: event[:name],
          address: create_city_address(event)
        }
      end
    end

    def create_city_address(event)
      #Je veux afficher l'adresse courte, je vais donc renvoyer la chaîne de caractères d'adresse après la ville, le quartier, la ville et le village
    end
  end
end

Le traitement qui gère les informations d'événement est dupliqué. Découpons cela comme suit et rendons-le commun.

area/common.rb


module Area
  class Common
    class << self
      def events(params)
        Api.call('/events', params)
      end

      def create_full_address(event)
        #Renvoie la chaîne complète d'adresses en combinant certaines informations
      end

      def create_city_address(event)
        #Je veux afficher l'adresse courte, je vais donc renvoyer la chaîne de caractères d'adresse après la ville, le quartier, la ville et le village
      end
    end
  end
end

L'appelant ressemble à ce qui suit.

area/pref.rb


require_relative 'common'

module Area
  class Pref
    def event
      events = Common.events(params)
      events.each_with_object([]) do |event, memo|
        memo << {
          name: event[:name],
          address: Common.create_city_address(event)
        }
      end
    end
  end
end

Je pensais que tout allait bien, mais ensuite il y avait une nouvelle classe pour gérer les pages de la station. Supposons que vous n'ayez pas besoin d'informations d'adresse sur cette page, vous n'avez besoin que d'informations sur la station.

La dernière fois, j'ai fait du commun sur des pages de zone comme area / common.rb. De plus, étant donné que les informations acquises sur la page de la zone sont inutiles et que les informations que je veux sont légèrement différentes, j'ai décidé de mettre en œuvre l'accès aux informations d'événements par moi-même.

traffic/station.rb


module Traffic
  class Station
    def event(params)
      events = Api.call('/events', params)
      events.each_with_object([]) do |event, memo|
        memo << {
          name: event[:name],
          stations: create_stations(event)
        }
      end
    end

    def create_stations(event)
      #Renvoie des informations sur la station
    end
  end
end

Ce qui précède est la situation générale avant la modélisation. L'exemple est long et j'estime qu'il n'est pas assez bon. Permettez-moi de prétendre que la situation est en fait un peu plus compliquée.

Problèmes avant la modélisation

Pour résumer les problèmes, on peut dire que la situation était la suivante.

Si l'API pratique dans l'entreprise est une ressource de données, une réponse avec un certain soin sera renvoyée, il peut donc y avoir des cas où elle est appelée dans la logique métier sans être correctement séparée. Je pense. Même si elle peut être mise en œuvre simplement au début, elle deviendra chaotique lorsque diverses utilisations commenceront à divers endroits.

Après la modélisation

Comme solution, j'ai décidé de faire fonctionner le modèle correctement. J'ai décidé de couper le modèle pour chaque ressource de données et d'accéder toujours à la ressource de données via le modèle. En consolidant la décoration de la réponse dans le modèle, nous avons essayé de la rendre commune et avons créé un état clair où l'implémenter.

Plus précisément, c'est comme suit.

Le modèle qui gère chaque événement est défini comme suit.

model/event.rb


module Model
  class Event
    attr_accessor :name, :address

    def initialtize(event)
      {
        'name' => '@name',
        'address' => '@address',
      }.each do |key, prop|
        instance_variable_set(prop, event[key])
      end
    end

    def full_address
      #Renvoie la chaîne complète d'adresses en combinant certaines informations
    end

    def city_address
      #Je veux afficher l'adresse courte, je vais donc renvoyer la chaîne de caractères d'adresse après la ville, le quartier, la ville et le village
    end

    def stations
      #Renvoie des informations sur la station
    end
  end
end

Le modèle qui gère la liste des événements est le suivant.

model/events.rb


require_relative 'event'

module Model
  module Events
    attr_accessor :events

    def initialize(events)
      @events = events.each_with_object([]) do |event, memo|
        memo << Model::Event.new(event)
      end
    end

    class << self
      def get(params)
        events = Api.call('/events', params)
        Model::Events.new(events)
      end
    end
  end
end

Le côté utilisateur ressemble à ceci.

area/pref.rb


module Area
  class Pref
    def event
      events = Model::Events.get(params)
      events.each_with_object([]) do |event, memo|
        memo << {
          name: event.name,
          address: event.full_address
        }
      end
    end
  end
end

Nous avons défini model / event.rb comme un modèle qui gère les informations d'événement. Celui-ci est responsable de la tenue et de la décoration des informations sur les événements, et tous les accès aux informations sur les événements passent par ce modèle.

Étant donné que la réponse de l'API pour obtenir l'événement est renvoyée sous forme de type de liste, définissez model / events.rb et gérez la réponse de l'API ici. À ce stade, les informations d'événement individuel sont transmises à event.rb pour la décoration, et events.rb contient le résultat sous forme de liste.

En conséquence, les utilisateurs pourront accéder aux éléments décorés sans en avoir conscience.

Évitez les graisses

En modélisant comme décrit ci-dessus, nous avons pu consolider l'accès et la décoration aux ressources de données.

D'un autre côté, avec ce design, je pense qu'il y a un souci que le modèle gonfle. Afin d'éviter le modèle FAT, nous prenons les mesures suivantes.

Bien qu'il s'agisse d'un délégué, il est efficace lorsqu'il existe d'autres modèles de festival autres que l'événementiel, et que vous souhaitez avoir une méthode similaire pour chacun.

model/event.rb


module Model
  class Event
    #Tenez pour juger la tenue de l'événement?La méthode est Model::Transférer vers Condition et utiliser cette implémentation
    delegate :hold?, to: Model::Condition
  end
end

Peut être appelé comme suit

area/pref.rb


module Area
  class Pref
    def hold_event?
      Model::Events.hold?
    end
  end
end

SpecialThanks Merci à @nazomikan pour avoir fourni le projet, HK et SS pour sa promotion, et tous les membres du département!

Recommended Posts

Une histoire qui sépare la logique métier et le modèle
[Modèle MVC] Un mémo qu'un débutant a appris MVC
Logique métier?
Une histoire qui a mis du temps à établir une connexion
Ruby sum est une histoire sobre et étonnante
Une histoire sur la création d'un Builder qui hérite du Builder
Classe et modèle
[Ruby] Un programme / concept qui combine each_with_index et recherche
[Rails] Volume qui affiche les favoris et une liste de favoris