我有一个PORO(普通旧Ruby对象)来处理一些业务逻辑。它接收一个ActiveRecord
对象并对其进行分类。为简单起见,请以下面的示例为例:
class Classificator
STATES = {
1 => "Positive",
2 => "Neutral",
3 => "Negative"
}
def initializer(item)
@item = item
end
def name
STATES.fetch(state_id)
end
private
def state_id
return 1 if @item.value > 0
return 2 if @item.value == 0
return 3 if @item.value < 0
end
end
但是,我还想根据这些state_id
&#34;虚拟属性&#34;进行分组对象的查询。我目前正在通过在SQL查询中创建此属性并在GROUP BY
语句中使用它来处理它。参见示例:
class Classificator::Query
SQL_CONDITIONS = {
1 => "items.value > 0",
2 => "items.value = 0",
3 => "items.value < 0"
}
def initialize(relation = Item.all)
@relation = relation
end
def count
@relation.select(group_conditions).group('state_id').count
end
private
def group_conditions
'CASE ' + SQL_CONDITIONS.map do |k, v|
'WHEN ' + v.to_s + " THEN " + k.to_s
end.join(' ') + " END AS state_id"
end
end
这样,我就可以将这个业务逻辑变成SQL,并以非常有效的方式进行这种查询。
问题是:我有重复的业务逻辑。它存在于&#34; ruby&#34;代码,对单个对象进行分类,也在&#34; SQL&#34;在数据库级别对对象集合进行分类。
这是一种不好的做法吗?有办法避免这种情况吗?我实际上能够做到这一点,做到以下几点:
item = Item.find(4)
items.select(group_conditions).where(id: item.id).select('state_id')
但是通过这样做,我失去了对未在数据库中持久化的对象进行分类的能力。另一种方法是使用Iterator对ruby中的每个对象进行分类,但之后我将失去数据库性能。
如果我需要两种情况中的最佳情况,那么保持重复的业务逻辑似乎是不可避免的。但我只是想确定这一点。 :)
谢谢!
答案 0 :(得分:0)
有没有机会在数据库中引入触发器?如果是这样,我会使用数据库中的“计算”字段state_id
来改变它在INSERT
和UPDATE
上的值(这将带来更高的生产力效益)和此代码红宝石:
def state_if
return @item.state_id if @item.state_id # persistent object
case @item.value
when 0 then 2
when -Float::INFINITY...0 then 3
else 1
end
end
答案 1 :(得分:0)
我宁愿保持数据库简单,并尽可能地将逻辑放在Ruby代码中。由于分类没有存储在数据库中,我不希望查询返回它。
我的解决方案是定义一个将包含在ActiveRecord模型类中的问题。
module Classified
extend ActiveSupport::Concern
STATES = {
1 => "Positive",
2 => "Neutral",
3 => "Negative"
}
included do
def state_name
STATES.fetch(state_id)
end
private
def state_id
(0 <=> value.to_i) + 2
end
end
end
class Item < ActiveRecord::Base
include Classified
end
我像往常一样从数据库中获取项目。
items = Item.where(...)
由于每个item
都知道自己的分类值,所以我不必向数据库询问它。
items.each do |item|
puts item.state_name
end
答案 2 :(得分:0)
ActiveRecord 本身意味着持久性和业务逻辑之间存在一定程度的耦合。然而,只要模式允许,如果你没有真正的性能限制,第一个选择应该是让你的持久性代码尽可能的愚蠢,并移动这个“分类”(它显然是一个业务规则)尽可能远离数据库。
基本原理是更改与数据库相关的代码成本更高(尤其是当您的系统已投入生产时),并且通常比纯业务逻辑更难测试且速度更慢。