DEV Community

Pedro Leonardo
Pedro Leonardo

Posted on

Associações polimórficas no Rails: como fazer, prós e contras

Recentemente, me deparei com uma situação no trabalho em que precisava implementar uma tabela que se relacionasse com várias outras tabelas diferentes, mas que possuía os mesmos campos em comum. Pesquisando sobre a melhor forma de lidar com isso no Rails, descobri as associações polimórficas

Neste artigo, vamos aprender na prática como implementá-las e discutir suas vantagens, desvantagens e quando usar.

O que são associações polimórficas?

Uma associação polimórfica é um recurso do Rails que permite que um mesmo modelo pertença a mais de um outro modelo, usando uma estrutura genérica.

Por exemplo, imagine que você tenha um sistema em que usuários e empresas podem ter contatos (e-mail, telefone). Em vez de criar duas tabelas (user_contacts e company_contacts), você pode ter uma tabela única de contacts que se relaciona com ambos.

Criando a associação polimórfica

Implementar uma associação polimórfica no Rails é simples. Vamos criar uma tabela contacts que poderá se relacionar tanto com users quanto com companies (ou qualquer outro modelo que precise de contatos).

Migration

class CreateContacts < ActiveRecord::Migration[7.1]
  def change
    create_table :contacts do |t|
      t.string :email
      t.string :phone

      t.references :contactable, polymorphic: true, null: false

      t.timestamps
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

O t.references :contactable, polymorphic: true cria duas colunas:

  • contactable_id (inteiro)
  • contactable_type (string)

Esses dois campos indicam quem é o dono do contato.

Models

class Contact < ApplicationRecord
  belongs_to :contactable, polymorphic: true
end

class User < ApplicationRecord
  has_many :contacts, as: :contactable
end

class Company < ApplicationRecord
  has_many :contacts, as: :contactable
end
Enter fullscreen mode Exit fullscreen mode

Agora, tanto User quanto Company podem ter múltiplos contatos.

Exemplo de uso

user = User.create(name: "Pedro")
company = Company.create(name: "ABC")

user.contacts.create(email: "pedro@email.com", phone: "1111-1111")
company.contacts.create(email: "contato@tech.com", phone: "2222-2222")

puts user.contacts.first.email     # => "pedro@email.com"
puts company.contacts.first.phone  # => "2222-2222"

Enter fullscreen mode Exit fullscreen mode

Com isso, conseguimos reutilizar a tabela contacts em diferentes contextos.

Vantagens

Reutilização: você cria uma única tabela para relacionar com vários modelos diferentes.

Menos duplicação: evita criar várias tabelas para armazenar dados semelhantes.

Flexibilidade: se amanhã surgir outro modelo que precise de contatos (ex: Suppliers), basta adicionar a associação.

Integração Rails: o suporte nativo facilita bastante, sem precisar reinventar a roda.

Desvantagens

Consultas mais complexas: filtrar dados polimórficos pode gerar queries mais pesadas.

Dificuldade em manter integridade: o Rails não cria foreign keys em colunas polimórficas (já que o campo pode apontar para várias tabelas diferentes).

Escalabilidade: se a tabela crescer demais com dados de muitos modelos, pode virar um gargalo.

Legibilidade: para quem não conhece o conceito, entender o contactable_type pode ser confuso.

Quando usar?

Use quando:

  • Você tem dados idênticos que precisam estar disponíveis em múltiplos modelos (ex: contatos, endereços, imagens, comentários).
  • A estrutura se repete bastante e faria pouco sentido criar tabelas separadas.

Evite quando:

  • Os dados variam muito entre os modelos (ex: se contatos de User e Company tivessem campos completamente diferentes).
  • Você precisa de restrições fortes no banco (integridade referencial por foreign keys diretas).
  • A tabela polimórfica tende a crescer demais, impactando performance.

Alternativa às associações polimórficas

Uma alternativa às associações polimórficas é criar os relacionamentos de forma explícita na tabela, adicionando colunas específicas como user_id, company_id, supplier_id, etc.

Exemplo de migration sem polimorfismo:

class CreateContacts < ActiveRecord::Migration[7.1]
  def change
    create_table :contacts do |t|
      t.string :email
      t.string :phone

      # Relacionamentos explícitos
      t.references :user, foreign_key: true
      t.references :company, foreign_key: true

      t.timestamps
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Prós dessa abordagem:

  • Performance melhor: consultas são mais rápidas porque não dependem de um campo type.
  • Integridade no banco: você consegue usar foreign keys diretamente, garantindo consistência.
  • Legibilidade: é mais fácil entender a relação olhando apenas a tabela.

Contras dessa abordagem:

  • Pouca escalabilidade: se amanhã surgir outro modelo (ex: Supplier), você precisa alterar a tabela e adicionar mais colunas.
  • Duplicação estrutural: quanto mais modelos diferentes, mais colunas redundantes terá na tabela.

Essa opção costuma ser escolhida em cenários onde performance é prioridade e o número de modelos relacionados é previsível e limitado.

Conclusão

Associações polimórficas são uma ferramenta poderosa no Rails. Elas trazem simplicidade e reuso quando bem aplicadas, mas podem gerar complexidade desnecessária e até impactar performance em cenários de alto volume de dados.

Uma alternativa válida é criar referências explícitas (user_id, company_id, etc.) em vez de usar o campo polimórfico. Essa abordagem melhora a performance e permite integridade via foreign keys, mas perde em flexibilidade e exige alterações na tabela sempre que surgir um novo modelo relacionado.

No fim, não existe solução única:

  • Se você precisa de flexibilidade e reuso → vá de associação polimórfica.
  • Se você precisa de performance e integridade forte no banco → prefira chaves explícitas (_id).

A escolha depende do contexto e das prioridades do projeto.

Top comments (3)

Collapse
 
ernanej profile image
Ernane Ferreira

Muito bom! Eu já usei polimórficas, mas realmente esbarrei na parte de performance. Achei ótima a comparação com relacionamentos explícitos, ajudou a clarear quando faz sentido usar cada abordagem. Obrigado por isso!

Collapse
 
thiteago profile image
Thiago David

Diferente em! Nunca tinha visto alguém usar isso ai antes, vou dar uma olhada depois. Valeu de mais por compartilhar.

Collapse
 
carloshendvpm profile image
Carlos Henrique

Muito obrigado por compartilhar!