2016-03-24 15 views
6

Tôi có một PORO (Plain Old Ruby Object) để xử lý một số logic nghiệp vụ. Nó nhận được một đối tượng ActiveRecord và phân loại nó. Vì lợi ích của sự đơn giản, đi sau là một ví dụ:Logic nghiệp vụ trùng lặp trong Ruby và 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 

Tuy nhiên, tôi cũng muốn làm các truy vấn mà nhóm đối tượng dựa trên những "thuộc tính ảo" state_id. Tôi hiện đang đối phó với điều đó bằng cách tạo thuộc tính này trong các truy vấn SQL và sử dụng nó trong các câu lệnh GROUP BY. Xem ví dụ:

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 

Bằng cách này, tôi có thể đưa logic kinh doanh này vào SQL và thực hiện loại truy vấn này một cách rất hiệu quả.

Vấn đề là: Tôi đã sao chép logic nghiệp vụ. Nó tồn tại trong "ruby" mã, để phân loại một đối tượng duy nhất và cũng trong "SQL" để phân loại một tập hợp các đối tượng ở cấp cơ sở dữ liệu.

Thực tiễn không tốt? Có cách nào để tránh điều này không? Tôi thực sự có thể thực hiện việc này, làm như sau:

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

Nhưng bằng cách này, tôi mất khả năng phân loại các đối tượng không được lưu giữ trong cơ sở dữ liệu. Cách khác sẽ phân loại từng đối tượng bằng ruby, sử dụng Iterator, nhưng sau đó tôi sẽ mất hiệu năng cơ sở dữ liệu.

Dường như không thể tránh khỏi việc giữ logic nghiệp vụ trùng lặp nếu tôi cần nhất trong hai trường hợp. Nhưng tôi chỉ muốn chắc chắn về điều này. :)

Cảm ơn!

Trả lời

0

Có cơ hội nào để giới thiệu trình kích hoạt trong cơ sở dữ liệu không? Nếu vậy, tôi sẽ đi với “tính” lĩnh vực state_id trong cơ sở dữ liệu, thay đổi giá trị của nó trên cả hai INSERTUPDATE (điều này sẽ mang lại lợi ích năng suất thậm chí nhiều hơn) và mã này trong ruby:

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

Nó có thể cải thiện hiệu suất bằng cách cập nhật trường này trong cơ sở dữ liệu. Tuy nhiên, tôi tin rằng tôi vẫn sẽ có logic kinh doanh trùng lặp, phải không? Cả trên kích hoạt cơ sở dữ liệu và phương thức 'state_if'. –

+0

@ JoãoDaniel có và không. Cho dù logic kinh doanh là hoàn toàn giống nhau cho các đối tượng liên tục và không liên tục, với cách tiếp cận này, bạn có thể đặt tất cả các logic vào lớp DB và làm 'start_transaction ⇒ read_state ⇒ rollback' hack. – mudasobwa

0

Tôi muốn thay vì giữ cơ sở dữ liệu đơn giản và đặt logic vào mã Ruby càng nhiều càng tốt. Kể từ khi phân loại không được lưu trữ trong cơ sở dữ liệu, tôi sẽ không mong đợi các truy vấn để trả lại nó.

Giải pháp của tôi là xác định mối quan tâm sẽ được đưa vào các lớp mô hình 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 

Và tôi tìm nạp các mục từ cơ sở dữ liệu như thường lệ.

items = Item.where(...) 

Vì mỗi item biết giá trị phân loại riêng của mình, tôi không cần phải hỏi cơ sở dữ liệu cho nó.

items.each do |item| 
    puts item.state_name 
end 
Các vấn đề liên quan