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!
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'. –
@ 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