I have taken over the code base for a ruby-on-rails application which relies very heavily on ActiveRecord callbacks to perform domain rules.
The application can be compared to a bank application, where a customer has an Account. On each account you can register a Transaction (an amount of money that is either inserted or removed from the account). The Account has an account_balance that is updated every time a transaction is created. This update happens as an ActiveRecord callback.
class Account < ActiveRecord::Base
has_many :transactions
def update_account_balance
account_balance = 0
self.transaction_logs.each do |transaction|
account_balance = account_balance + (transaction.amount + transaction.commission)
end
self.account_balance = account_balance
end
end
class Transaction < ActiveRecord::Base
belongs_to :account
after_save :update_account_value
def update_account_value
self.account.update_account_balance
self.account.save!
end
end
I find that this way of coding makes it very difficult to figure out what happens (and what goes wrong when a bug occurs). Also, since the account needs to load all transactions, this will be heavier as more and more transactions are added.
Had I been developing an application like this in .NET (in which I am much more familiar) I would have created something like this:
public class Account {
public void AddTransaction(decimal amount) {
this.Transactions.Add(new Transaction(amount));
this.AccountValue += amount;
}
}
And then made sure that a Transaction object is immutable.
I am very tempted to refactor the system into something like that. But should I? Would that be the "rails way"?
The drawback, as far as I can tell, is that my controller needs to explicitly handle controlling database transactions, something which happened implicitly in the Transaction.save! call currently called in the application. Are there other factors I should be aware of?