request-quote
Ruby on Rails

When Fat Models Are No Big Deal

By Alex K. December 29th, 2015

Working on the RoR project with ActiveRecord we noticed an increase in the number of models (more than 1000 lines). So it was getting more difficult to add new functionality. The project scope was increasing as well so we had to find a solution to the problem.

We decided to spend some time on refactoring. After fat model code analysis we learnt that database query methods had taken 30% of the overall code. 

class FirstModel < ActiveRecord::Base
# relations section
# ...
    
  # query methods
  class << self
    # ...
    def custom_method_to_fetch_data
      joins(:some_relation)
      .where(field1: value)
      .group(:field2)
      .order(field3: :desc)
    end
    
    def another_custom_method_to_fetch_data
      # ...
    end
    # ...
  end
  
  # here go object methods
  # ........
end

 

Therefore, it became really important to find out how to reduce the number of lines and  to separate query methods from object methods more logically.

As a result, two variants were left to consider:

1. Use of Repository pattern

2. Use Query Object pattern

Repository

“Repository mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects,” according to Martin Fowler’s ‘Patterns of Enterprise Application Architecture.’

ActiveRecord::Querying component which is included in ActiveRecord::Base provides us with collection-like interface for accessing domain.

It’s not the aim of intermediate refactoring to separate collection-like interface from ActiveRecord::Base. So it became clear that Repository pattern did not work.

Query object

In his book ‘Patterns of Enterprise Application Architecture’ Martin Fowler says, “Query object is an object that represents a database query.”

Query Object pattern means that each request to the database should  be treated as a separate object. Such RoR practice is quite common and recommended to use on the Web.

Though the second approach seemed good it did not fit our time frame. 

Query mixins  

Eventually we decided to move all the methods from “fat” model to mixin.

New folder app/models/querying was created.

Then we created the file first_model.rb and moved there all query methods from the model.

module Querying
  module FirstModel
    # ...
    def custom_method_to_fetch_data
      # ...
    end
    
    def another_custom_method_to_fetch_data
      # ...
    end
    # ...
  end
end

The model was extended with Querying::FirstModel :

class FirstModel < ActiveRecord::Base
  # query methods
  extend Querying::FirstModel
  # relations section
  # ...
  # here go object methods
  # ........
end

As a result, it became possible to use the model. 

data = FirstModel.custom_method_to_fetch_data

 

However, the final approach we took has its own positive and negative results:

Advantages:

1. The model code is distributed to different files which makes navigation easier.

2. Inverse compatibility is not broken.

3. Refactoring process takes little time.

Disadvantages:

1. Superficial refactoring.

2. The actual model is still fat and it keeps all multiple responsibilies which were available before refactoring.

3. “Extract Mixins from Fat Models” approach does not contribute to the model decomposition.

 

Conclusion

Thus, query mixin is a good approach for fast and superficial refactoring but it is better not to use it on a long-term and regular basis. We advise to use Query Object or change ORM. 

Alex K.

Alex K.

Senior Ruby on Rails Developer at iKantam

Browse Recent Posts