Проверка и обработка ошибок для объектов службы

Я создал объект службы в Rails для работы в качестве интерфейса между нашим приложением и нашим API.

Я получил идею от http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

Вот небольшой пример:

class PackagesService
  def self.get_package(package_id)
    raise ArgumentError.new("package_id can't be nil") if package_id.blank?
    package = API::get "/packages/#{package_id}"
    package = JSON.parse package,
                          :symbolize_names => true unless package.blank?

  end
end

Есть ли хороший шаблон для обработки валидации и/или ошибки бросания для объектов службы?

Для валидаций:

  • Мне нужно проверить все входы для нулевого или неправильного типа. Есть ли способ упростить проверку? Возможно, расширение рельсов?

Для ошибок:

  • Я мог поймать все ошибки API, а затем безопасно вернуть нуль. Но программист, использующий служебный объект, может не знать значения nil.
  • Я мог поймать ошибки API и поднять еще одну ошибку, что означает дополнительные усилия для этого во всех функциях.
  • Третий вариант - оставить его как есть и позволить программисту обрабатывать все ошибки API.

Сообщите мне, знаете ли вы хороший образец или у вас есть идеи по интерфейсу API.

Ответ 1

Для простых случаев (например, только с одним аргументом), ваш чек-рейз с ArgumentError в порядке. Как только вы начинаете создавать сложные случаи (несколько аргументов, объектов и т.д.), Я начинаю полагаться на Virtus и Проверки ActiveModel.

Ваша связанная статья фактически упоминает их (см. "Извлечь объекты формы" ). Я иногда использую что-то вроде этого для создания служебных объектов, например.

require 'active_model'
require 'virtus'

class CreatePackage
  include Virtus
  include ActiveModel::Validations

  attribute :name, String
  attribute :author, String
  validates_presence_of :name, :author

  def create
    raise ArgumentError.new("Invalid package") unless self.valid?
    response = JSON.parse(
      API::post("/packages", self.attributes),
      :symbolize_names => true
    )
    Package.new(response)
  end
end

class Package
  include Virtus
  attribute :id, Integer
  attribute :name, String
  attribute :author, String
end

# eg.
service = CreatePackage.new(
  :name => "Tim Tams",
  :author => "Tim",
)
service.valid? # true; if false, see service.errors
package = service.create

package.attributes
# => { :id => 123, :name => "Tim Tams", :author => "Tim" }

Что касается исключений, я бы оставил их как есть для меньших действий (например, этот класс обслуживания). Я мог бы обернуть их, если я напишу что-то более существенное, например, всю клиентскую библиотеку API.

Я бы никогда не вернул ноль. Такие вещи, как сетевая ошибка или плохой или непроверяемый ответ с сервера, имеют явные ошибки.


Наконец, существует более тяжелый подход, называемый use_case. Даже если вы не используете его, у него есть куча идей о том, как решать объекты обслуживания, проверки и результаты, которые могут вам показаться интересными.

Изменить. Также проверьте Mutations. Как use_case, кроме более простых и менее всеобъемлющих.