2016-03-24 4 views
6

ビジネスロジックを扱うPORO(Plain Old Ruby Object)があります。 ActiveRecordオブジェクトを受信し、分類します。簡略化のため、一例として、以下の取る:RubyとSQLのビジネスロジックが重複しています

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「仮想属性」に基づいてクエリをしたいです。私は現在、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にこのビジネスロジックを取得し、非常に効率的な方法で、クエリのこの種を作ることができます。

問題は次のとおりです。ビジネスロジックが重複しています。 "ルビー"コード内に存在し、単一のオブジェクトを分類し、また "SQL"に分類して、データベースレベルのオブジェクトの集合を分類する。

これは悪い習慣ですか?これを避ける方法はありますか?私は実際には、これを行うことができた次の操作を行う:

item = Item.find(4) 
items.select(group_conditions).where(id: item.id).select('state_id') 

をしかし、これを行うことによって、私は、データベースに永続化されていないオブジェクトを分類する能力を失います。もう1つの方法は、Iteratorを使用してルビの各オブジェクトを分類することですが、データベースのパフォーマンスが低下します。

2つのケースの中でベストが必要な場合は、重複したビジネスロジックを維持することは避けられないようです。しかし、私はちょうどこれについて確かめたいです。 :)

ありがとう!

答えて

0

データベースにトリガを導入する機会はありますか?

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 
+0

データベースのこのフィールドを更新すると、パフォーマンスが向上する可能性があります。しかし、私はまだビジネスロジックが重複していると思います。データベーストリガと 'state_if'メソッドの両方。 –

+0

@JoãoDanielはい、いいえ。この方法では、ビジネスロジックが永続オブジェクトと非永続オブジェクトで絶対に同じであるかどうかにかかわらず、すべてのロジックをDBレイヤに入れ、 'start_transaction⇒read_state⇒rollback'ハックを実行することができます。 – mudasobwa

0

私がしたい:もしそうなら、私はそれの両方INSERTUPDATE上の値(これは、より生産性の利益をもたらす)とルビーでこのコードを変更し、データベース内の「計算」フィールドstate_id、となるだろうデータベースをシンプルに保ち、可能な限り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 
関連する問題