Поиск…


замечания

Это похоже на простое дело, но когда вы начинаете занятия в классе, вы будете благодарны, что потратили время на их организацию.

Модельный класс

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :comments

  validates :user, presence: true
  validates :title, presence: true, length: { in: 6..40 }

  scope :topic, -> (topic) { joins(:topics).where(topic: topic) }

  before_save :update_slug
  after_create :send_welcome_email

  def publish!
    update(published_at: Time.now, published: true)
  end

  def self.find_by_slug(slug)
    find_by(slug: slug)
  end

  private

  def update_slug
    self.slug = title.join('-')
  end

  def send_welcome_email
    WelcomeMailer.welcome(self).deliver_now
  end
end

Модели обычно отвечают за:

  • установление отношений
  • проверка достоверности данных
  • обеспечение доступа к данным по областям и методам
  • Выполнение действий по сохранению данных.

На самом высоком уровне модели описывают концепции домена и управляют их персистентностью.

Класс обслуживания

Контроллер является точкой входа в наше приложение. Однако это не единственная возможная точка входа. Я хотел бы получить доступ к моей логике:

  • Рейк-задачи
  • фоновые задания
  • приставка
  • тесты

Если я введу свою логику в контроллер, она не будет доступна из всех этих мест. Итак, давайте попробуем «тощий контроллер, толстая модель» и переместим логику к модели. Но какой? Если данный фрагмент логики включает модели User , Cart и Product - где он должен жить?

Класс, который наследует от ActiveRecord::Base уже имеет много обязанностей. Он обрабатывает интерфейс запроса, ассоциации и проверки. Если вы добавите еще больше кода в свою модель, он быстро станет недостижимым беспорядком с сотнями общественных методов.

Служба - это обычный объект Ruby. Его класс не должен наследовать ни от какого конкретного класса. Его имя - это глагольная фраза, например CreateUserAccount а не UserCreation или UserCreationService . Он живет в каталоге приложений / сервисов. Вы должны создать этот каталог самостоятельно, но Rails будет автоматически загружать классы внутри вас.

Объект службы делает одну вещь

Объект службы (объект метода) выполняет одно действие. Он выполняет бизнес-логику для выполнения этого действия. Вот пример:

# app/services/accept_invite.rb
class AcceptInvite
  def self.call(invite, user)
    invite.accept!(user)
    UserMailer.invite_accepted(invite).deliver
  end
end

Ниже приведены три конвенции:

Услуги идут под app/services directory . Я рекомендую вам использовать подкаталоги для бизнес-логики. Например:

  • Файл app/services/invite/accept.rb будет определять Invite::Accept пока app/services/invite/create.rb определит Invite::Create
  • Услуги начинаются с глагола (и не заканчиваются Сервисом): ApproveTransaction , SendTestNewsletter , ImportUsersFromCsv
  • Услуги отвечают на метод call . Я обнаружил, что использование другого глагола делает его немного избыточным: ApproveTransaction.approve() не читается хорошо. Кроме call метод call является де-факто-методом для объектов lambda , procs и методов.

Выгоды

Объекты службы показывают, что делает мое приложение

Я могу просто взглянуть на каталог служб, чтобы узнать, что делает мое приложение: ApproveTransaction , CancelTransaction , BlockAccount , SendTransactionApprovalReminder ...

Быстрый взгляд на объект службы, и я знаю, что такое бизнес-логика. Мне не нужно проходить через контроллеры, обратные вызовы модели ActiveRecord и наблюдатели, чтобы понять, что подразумевает «одобрение транзакции».

Модифицированные модели и контроллеры

Контроллеры превращают запрос (params, session, cookies) в аргументы, передают их службе и перенаправляют или обрабатывают в соответствии с ответом службы.

class InviteController < ApplicationController
 def accept
    invite = Invite.find_by_token!(params[:token])
    if AcceptInvite.call(invite, current_user)
      redirect_to invite.item, notice: "Welcome!"
    else
      redirect_to '/', alert: "Oopsy!"
    end
  end
end

Модели имеют дело только с ассоциациями, областями, валидациями и настойчивостью.

class Invite < ActiveRecord::Base
  def accept!(user, time=Time.now)
    update_attributes!(
      accepted_by_user_id: user.id,
      accepted_at: time
    )
  end
end

Это упрощает тестирование и обслуживание моделей и контроллеров!

Когда использовать класс обслуживания

Достижение для объектов службы, когда действие соответствует одному или нескольким из следующих критериев:

  • Действие является сложным (например, закрытие книг в конце отчетного периода)
  • Действие распространяется на несколько моделей (например, покупка электронной коммерции с использованием объектов Order, CreditCard и Customer)
  • Действие взаимодействует с внешней службой (например, публикация в социальных сетях)
  • Действие не является основной проблемой базовой модели (например, сбрасывание устаревших данных через определенный период времени).
  • Существует несколько способов выполнения действия (например, аутентификация с помощью токена доступа или пароля).

источники

Блог Адама Недзельского

Блог Brew House

Блог о климате Code



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow