【问题标题】:Lock association inside callback with ActiveRecord?使用 ActiveRecord 锁定回调内的关联?
【发布时间】:2021-03-11 17:55:20
【问题描述】:

是否允许在 ActiveRecord 的回调中锁定关联对象?

class Product < ApplicationRecord
  # stock (integer)

  has_many :sales
end

class Sale < ApplicationRecord
  # quantity (integer)
  
  belongs_to :product

  before_save do
    self.product.lock!
    throw :abort if self.product.stock < self.quantity
  end
end

这个想法是防止创建库存不足的销售记录。

【问题讨论】:

    标签: ruby-on-rails ruby activerecord


    【解决方案1】:

    “允许”可能是,但 IMO 这不是一个好习惯。这就是我认为的原因:

    • Sale 不应该知道lock product,不应该知道product 属性。它应该尽可能地愚蠢。
    • Sale 不应该规定它是否可以保存。 IMO 应该更多地确认产品已售出。
    • lockPessimistic 并且必须包含在transaction 中。我没有看到整个流程,但如果不是,那么回滚将是不完整的(它发生在我身上)

    在处理销售、付款(微妙的业务流程)时,我通常将代码提取到 Service Object 中。可以单独测试,不要用超出范围的逻辑污染模型,遵循代码质量原则等。

    这是一个经验例子:

    class PurchaseProduct
      def initialize(product, quantity)
       @product = product
       @quantity = quantity
      end
    
      def purchase
        Product.transaction do 
          @product.lock!
          if @product.stock >= @quantity
            product.update_attribute(:stock, @product.stock - @quantity)
            product.sales.create(quantity: @quantity)
            # other stuff to do 
            # send confirmation email, etc
          else
            throw "Something"
          end
        end
      end
    end
    

    在编写软件时,一个非常重要的方面是具有松散耦合的代码。如果您不熟悉,可以查看Principle of least knowlede 了解更多信息。 SRP也不错。

    【讨论】:

      【解决方案2】:

      你可能想要custom validate:

      class Sale < ApplicationRecord
        belongs_to :product
      
        validate :valid_quantity
      
        private
      
        def valid_quantity
          if quantity > product.stock
            errors.add(:quantity, "can't be greater than product stock")
          end
        end
      end
      

      通过此验证,Sale 不会在库存不足的情况下创建,并且会显示描述性错误消息。

      【讨论】:

      • 该站点很可能是电子商务站点,因此很可能会发生竞争条件,因此lock。此验证适用于简单场景,但当多个用户在黑色星期五购买相同商品时会失败:D
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多