移动事务操作远离控制器(Moving transactional operations away f

2019-10-20 12:18发布

  def cancel

    begin
      to_bank = @transfer.main_to_bank
      to_bank.with_lock do
        to_bank.locked_balance -= @transfer.amount
        to_bank.available_balance += @transfer.amount
        to_bank.save!
        @transfer.cancel
        @transfer.save!
      end
    rescue ActiveRecord::ActiveRecordError => e
      redirect_to admin_transfer_url(@transfer), alert: "Error while cancelling."
      return
    end

    redirect_to admin_transfer_url(@transfer), notice: 'Transfer was successfully cancelled.'
  end

我想重构上面的代码到传输模式或其他一些地方,因为这个相同的代码在其他地方使用。 然而,ActiveRecord的模型是否内的一些交易魔术,所以我很担心我可能会通过简单的模式下,移动代码引入一些意想不到的副作用。

是我的忧虑毫无根据,以及如何将上面的代码通常被重构到外面的控制器可重用性?

更新:这似乎是对服务对象的理想场所,这里描述http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ 。

Answer 1:

1)当你在你的更新提,这是一个非常适合的服务对象。 把它们像应用程序/服务的目录,因为在/ app什么是自动加载。 有实现它们的两种常用方法:

作为一个静态类:

AccountService.transfer(from_account, to_account, amount)

作为一个对象:

service = AccountService.new(from_account, to_account)
service.transfer(amount)

我宁愿选择一个从Java企业开发的背景,你会使用的Java bean同样的到来。

我也建议所有的服务作为一项规则返回结果的对象。 这意味着你创建一个小型的类名为“ServiceResult”,其中包含呼叫是否是成功的,用户友好的消息的布尔标志和可选的结果对象(这是,如果你没有服务结果对象方法的返回值)。 换句话说检查从控制器或任何其他地方将是结果:

response = AccountService.transfer(from, to, amount)

if response.success?
  flash[:notice] = response.message
else
  flash[:alert] = response.message
end

你总是可以重构为一个方法是:

flash_service_result response

添加一些辅助方法,以您的服务后,服务方法看起来是这样的:

def self.transfer(from_account, to_account, amount)

   ActiveRecord::Base.transaction do
     ..do stuff..
     from_account.save!
     to_account.save!

     service_success("Transfer succesfull...")
   end

 rescue SomeException => error
   service_error("Failed to transfer...")

 rescue ActiveRecord::RecordInvalid => invalid_record_error
   service_validation_error(invalid_record_error)
 end

当使用结果对象,永远不会提高从你期望得到处理的服务的异常(检查其他语言的例外)。

2)使用活动记录的交易方法将具有相同的行为无论在哪里,它是从调用。 它不会添加任何副作用。 所以,是的,你可以从一个控制器或服务调用它。



文章来源: Moving transactional operations away from the controller