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
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
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"
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
eCompany
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
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)
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!
Diferente em! Nunca tinha visto alguém usar isso ai antes, vou dar uma olhada depois. Valeu de mais por compartilhar.
Muito obrigado por compartilhar!