我在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
是否有任何良好的模式来处理验证和/或抛出Service对象的错误?
验证码:
>我必须检查所有的输入为零或错误的类型.有什么办法容易验证吗?也许是铁路延伸?
出错:
>我可以抓住所有的API错误,然后安全地返回一个零.但使用服务对象的程序员可能不知道nil的含义.
>我可以抓住API错误,并引发另外一个错误,这意味着在所有功能中都可以做到这一点
>第三个选项是保持原样,让程序员处理所有的API错误.
让我知道,如果你知道任何好的模式,或者你有更好的想法来连接一个API.
解决方法
对于简单的例子(例如,只有一个参数),那么你的ArgumentError的检查和加注就可以了.一旦你开始复杂的情况(多个参数,对象等),我开始倾向于
Virtus和
ActiveModel Validations.
Your linked article实际上提到了这些(参见“提取表单对象”).我有时会使用这样的东西来构建服务对象,例如.
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 end # eg. service = CreatePackage.new( :name => "Tim's Tams",:author => "Tim",) service.valid? # true; if false,see service.errors package = service.create package.attributes # => { :id => 123,:name => "Tim's Tams",:author => "Tim" }
在例外情况下,我会将它们按原样放在较小的动作上(像这个服务类).如果我正在写一些更实质的东西,如整个API客户端库,我将会包装它们.
我永远不会回到零.像网络错误,或者服务器的错误或不可稀释的响应都会受益于显式错误.
最后,还有一个更重的方法叫做use_case.即使你不使用它,它有一堆想法来解决你可能会感兴趣的服务对象,验证和结果.
编辑:另外,查看Mutations.像use_case,除了更简单和更全面.