有哪些常见模式可确保跨多个系统的Rails模型的数据持久性?

时间:2012-06-07 21:13:57

标签: ruby-on-rails persistence rails-activerecord

我们假设我们有以下情况:

class MyModel < ActiveRecord::Base
  after_save :throw_after_save
  after_commit :throw_after_commit

  private
    def throw_after_save
      raise "raising on after_save"
    end

    def throw_after_commit
      raise "raising on after_commit"
    end
end

class MyController < ApplicationController
  def callback
    begin
      MyModel.new(params).save
    rescue
      flash[:alert] = "Failed persisting to external system. Try again."
      Airbrake.notify(
        error_class: "External System Persistence",
        error_message: "External System Persistence: Failed to persist data",
        parameters: params
      )
    end

    redirect_to root_path
  end
end

我们从外部系统获得回调(用户填写一些数据并为帐户创建一组临时属性)。

让我们假设我们想在回调给我们之后在本地保留一些数据。保留此数据后,我们希望调用外部系统来完成帐户创建过程。外部系统应返回一个结果,通知我们需要在本地持久存储的一些其他数据。我们也知道在某些例外情况下,远程系统上的持久性不会成功发生(让我们说系统不可用,或者说它们出现问题)。

目标是捕获外部持久性异常以及成功并采取相应行动。在成功的情况下,一切都是笨拙的:附加数据存储在本地,redirect_to root_path发生。但是,在例外的情况下,我们想向用户表明这一点(可能设置一个flash[:alert]在视图中显示。)

我们曾尝试使用ActiveRecord::Callbacks从模型after_saveafter_commit中抛出异常,并通过设置警报并可能将异常传递给某个异常来在控制器处理该异常通知系统(如Airbrake)。在after_save的情况下,模型抛出异常并由控制器捕获,但记录未保存(即使在外部系统出现异常的情况下,我们也要求存储部分数据) - 这是不可接受的)。在after_commit的情况下,控制器抛出并拾取的例外,但部分记录是持久的。这意味着我们无法通知用户异常(除非我们实施一些通知推送机制 - 这是一种过度杀伤)。

事实证明,我们可以after_save上的模型上设置错误,这很好。但是,这是处理这种情况的良好的一般模式吗?

1 个答案:

答案 0 :(得分:1)

你没有很多选择。由于你所要求的逻辑本身很复杂,我会移动代码将记录保存到其他地方(可能是一个类方法或另一个类,代表一个服务),当有人想要保存这个特定的对象时,它会有通过这种服务模式。

例如:

class MyModelPersistenceService

    def save_model( model )
      result = model.save
      if result
        call_external_service
      end
    end 

end

Services在Ruby / Rails项目中并不常见,但它们非常适合这种用法(滥用AR回调通常会导致难以测试对象)。