Where do query-building functions live?

Programming Phoenix briefly mentions that functions which only build queries - generally ones that take an Ecto.Queryable in first position and return one as well - be put in the schema file.

This makes sense to me as queries tend to be strongly coupled to the schema definitions, by using literal field names and associations.

OTOH, anybody’s who has worked on a long-lived app that uses ActiveRecord knows how lengthy the list of named_scopes can get. Is there a point where a schema file is “too long”?

One approach I feel decent about to keep this under control is to relocate one-off query-building functions from the schema file to a private function in the context, to avoid sending readers off to another file to understand what’s happening.

Thoughts? Other options? Horror stories?


I try to keep reusable queries in the schema. I actually never start there though.

I tend to have my functions on the context delegate to a module that represents that use case. It only has one public function. I put my queries in there to start. If that code never becomes reusable, it stays local to the place it’s used, which is the best outcome in my opinion.

On the other spectrum, where something is reusable, but also very complex, I’ll probably have moved it to the schema, but then decided it’s too complex to be there. In that case, I’ll create a module just for that query. It’ll have one public function that takes all the parameters and returns a query. Then I’ll delegate from the schema to that function. This is useful for something like search, which can take a lot of options that equate to many different parts of a SQL query.

1 Like