Possiedo un oggetto PORO (Plain Old Ruby Object) per gestire alcune logiche di business. Riceve un oggetto ActiveRecord
e lo classifica. Per motivi di semplicità, prendo il seguente come esempio:Logica aziendale duplicata in Ruby e 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
Tuttavia, ho anche voglia di fare le query che raggruppa gli oggetti sulla base di questi "attributo virtuale" state_id
. Attualmente mi occupo di questo creando questo attributo nelle query SQL e utilizzandolo nelle istruzioni GROUP BY
. Vedere l'esempio:
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
In questo modo, posso ottenere questa logica di business in SQL e fare questo tipo di query in un modo molto efficiente.
Il problema è: ho duplicato la logica aziendale. Esiste nel codice "ruby", per classificare un singolo oggetto e anche in "SQL" per classificare una collezione di oggetti a livello di database.
È una cattiva pratica? c'è un modo per evitarlo? Io in realtà era in grado di farlo, facendo quanto segue:
item = Item.find(4)
items.select(group_conditions).where(id: item.id).select('state_id')
Ma facendo questo, perdo la possibilità di classificare gli oggetti che non sono persistevano nel database. L'altra via d'uscita sarebbe classificare ogni oggetto in ruby, usando un Iterator, ma poi perderei le prestazioni del database.
Sembra che sia inevitabile mantenere una logica aziendale duplicata se ho bisogno del meglio dei due casi. Ma voglio solo essere sicuro di questo. :)
Grazie!
Probabilmente migliorerebbe le prestazioni aggiornando questo campo nel database. Tuttavia, credo che avrei ancora una logica commerciale duplicata, giusto? Sia sul trigger del database che sul metodo 'state_if'. –
@ JoãoDaniel si e no. Se la logica aziendale è assolutamente la stessa per gli oggetti persistenti e non persistenti, con questo approccio potresti mettere tutta la logica nel livello DB e fare 'start_transaction ⇒ read_state ⇒ rollback' hack. – mudasobwa