Les Anti-Patrons de Rails dans les Modèles – et comment les corriger

Rails encourage la séparation claire des préoccupations, mais il est facile de laisser la logique fuir dans la mauvaise couche. Le modèle est censé encapsuler les règles métier et le comportement du domaine, mais de nombreuses applications se retrouvent avec de gros contrôleurs, des vues extrêmement chargées, ou des modèles trop complexes. Voici ci-dessous les anti-patrons courants dans les modèles Rails et leurs solutions, avec du code.


1. La logique de vue appartient au modèle

Une erreur courante consiste à placer une logique métier conditionnelle directement dans les vues.

Mauvais :

<% if @order.total > 100 %>
  <p>Commande Premium</p>
<% end %>

Mieux : Déplacer la règle dans le modèle.

class Order < ApplicationRecord
  def premium?
    total > 100
  end
end
<% if @order.premium? %>
  <p>Commande Premium</p>
<% end %>

2. Des callbacks qui en font trop

Les callbacks (rappels) sont utiles, mais en abuser cache un comportement important.

Mauvais :

class User < ApplicationRecord
  after_create :send_welcome_email

  def send_welcome_email
    Mailer.welcome(self).deliver_now
  end
end

Mieux : Extraire dans un objet de service.

class UserSignup
  def self.call(user)
    Mailer.welcome(user).deliver_now
  end
end
user = User.create!(params)
UserSignup.call(user)

3. Du SQL dans les contrôleurs

La logique de requête encombre les contrôleurs et disperse les règles métier.

Mauvais :

@active_users = User.where("last_login > ?", 30.days.ago)

Mieux : Utiliser une portée (scope) dans le modèle.

class User < ApplicationRecord
  scope :recently_active, -> { where("last_login > ?", 30.days.ago) }
end
@active_users = User.recently_active

4. Attributs sérialisés au lieu de tables

Le déversement de hachages dans les colonnes de type [serialize](https://api.rubyonrails.org/classes/ActiveModel/Serialization.html) rend les requêtes difficiles.

Mauvais :

class User < ApplicationRecord
  serialize :preferences, Hash
end

Mieux : Normaliser avec une association.

class Preference < ApplicationRecord
  belongs_to :user
end

class User < ApplicationRecord
  has_many :preferences
end

5. Abuser de l’héritage d’une seule table (STI)

STI force des sous-classes non liées dans une seule table, souvent avec de nombreuses colonnes NULL.

Mauvais :

class Payment < ApplicationRecord; end
class CreditCardPayment < Payment; end
class BankTransferPayment < Payment; end

Mieux : Utiliser le design polymorphe ou de composition.

class Payment < ApplicationRecord
  belongs_to :payable, polymorphic: true
end

class CreditCard < ApplicationRecord
  has_many :payments, as: :payable
end

Cet article a été inspiré par le livre Rails AntiPatterns: Best Practice Ruby on Rails Refactoring par Chad Pytel et Tammer Saleh. Si vous souhaitez lire le livre complet, vous pouvez le trouver sur Amazon : Rails AntiPatterns

Si vous achetez le livre via ce lien Amazon, je peux gagner une petite commission sans frais supplémentaires pour vous — merci de soutenir ce contenu.


À retenir

  • Déplacez les règles métier vers les modèles (pas dans les vues ni les contrôleurs).
  • Utilisez des objets de service lorsque les callbacks cachent trop de choses.
  • Gardez le SQL dans les scopes, plutôt que des requêtes dispersées.
  • Normalisez les structures de données, évitez les blobs sérialisés.
  • Évitez les abus de STI ; préférez une conception polymorphe ou compositionnelle.

En refactorisant ces anti-patrons, vos modèles restent expressifs, vos contrôleurs minces et vos vues propres.