Pergunta

Eu têm lutado nos últimos algumas horas pensando sobre qual a rota que eu deveria ir. Eu tenho um modelo de notificação. Até agora eu usei uma coluna NOTIFICATION_TYPE para gerenciar os tipos, mas eu acho que vai ser melhor para criar classes separadas para os tipos de notificações como eles se comportam de forma diferente.

Agora, há 3 maneiras notificações podem se enviados: SMS, Twitter, e-mail

Cada notificação teria:

id
subject
message
valediction
sent_people_count
deliver_by
geotarget
event_id
list_id
processed_at
deleted_at
created_at
updated_at

Parece que STI é um direito bom candidato? Claro Twitter / SMS não terá um tema e Twitter não terá um sent_people_count, valediction. Eu diria que, neste caso, eles compartilham a maior parte de seus campos. No entanto o que se eu adicionar um campo "reply_to" para o Twitter e um booleano para o DM?

Meu ponto aqui é que agora STI faz sentido, mas é este um caso em que eu possa ser me chutando no futuro para não apenas começando com MTI?

Para complicar ainda mais as coisas, eu quero um modelo de boletim que é um tipo de notificação, mas a diferença é que ele não vai usar event_id ou deliver_by.

Eu podia ver todas as subclasses de notificação usando cerca de 2/3 dos campos de classe base de notificação. É STI um acéfalo, ou devo usar MTI?

Foi útil?

Solução

Dada a informação limitada, eu diria vara com STI.

A pergunta-chave é: Há lugares em seu aplicativo onde você quer considerar todos os tipos de notificações juntos? Se assim for, então isso é um forte sinal de que você querer ficar com STI.

Outras dicas

Você já considerou a abordagem modelo misto?

Onde você usa herança única tabela para os seus campos de notificação do núcleo. Em seguida, descarregar todos os itens exclusivos para específicas mesas / modelos em um pertence a / tem um relacionamento com seus subclasses de notificação.

É um pouco mais sobrecarga de configurar, mas trabalha-se bastante seco, uma vez que todas as classes e tabelas estão definidos. Parece ser uma maneira muito eficiente de armazenar coisas. Com o carregamento ansioso você não deve estar causando tensão muito adicional no banco de dados.

Para os fins deste exemplo, vamos supor que e-mails não temos detalhes únicos. Aqui está como ele mapeia.

class Notification < ActiveRecord::Base
  # common methods/validations/associations
  ...

  def self.relate_to_details
    class_eval <<-EOF
      has_one :details, :class_name => "#{self.name}Detail"
      accepts_nested_attributes_for :details
      default_scope -> { includes(:details) }
    EOF
  end
end

class SMS < Notification
  relate_to_details

  # sms specific methods
  ...
end

class Twitter < Notification
  relate_to_details

  # twitter specific methods
  ...
end

class Email < Notification

  # email specific methods
  ...
end

class SMSDetail < ActiveRecord::Base
  belongs_to :SMS, :class_name => "SMS"           

  # sms specific validations
  ...
end

class TwiterDetail < ActiveRecord::Base
  belongs_to :twitter

  # twitter specific validations
  ...
end

Cada uma das tabelas de detalhes irá conter um ID notificação e apenas colunas que forma de necessidades de comunicação que não está incluído na tabela de notificações. Embora isso significaria uma chamada de método extra para obter meios de informação específica.

Este é ótimo saber, mas você acha que é necessário?

Muito poucas coisas são necessárias em termos de design. Como CPU e soltar o espaço de armazenamento no custo faça assim aquelas conceitos de design necessárias. Propus este esquema porque oferece o melhor de ambos STI e MTI, e remove algumas de suas fraquezas.

Quanto vantagens ir:

Este regime prevê a consistência do STI. Com tabelas que não precisam ser recriados. A tabela vinculada fica em torno de dezenas de colunas em que estão vazias em 75% de suas linhas. Você começa também a criação subclasse fácil. Onde você só precisa criar uma correspondência Detalhes tabela se o novo tipo não é completamente coberto pelos campos de notificação básicas. Ele também mantém a iteração sobre todas as notificações simples.

De MTI, você começa a poupança de armazenamento e facilidade de personalização no atendimento das necessidades de uma classe sem a necessidade de redefinir as mesmas colunas para cada novo tipo de notificação. Somente os únicos.

No entanto, este esquema também carrega sobre a grande falha com STI. A tabela vai substituir 4. Que pode começar a causar desaceleração uma vez que fica enorme.

A resposta curta é, sem esta abordagem não é necessário. Eu vejo isso como a maneira mais DRY para lidar com o problema de forma eficiente. A muito curto prazo STI é a maneira de fazê-lo. No MTI muito longo prazo, é o caminho a percorrer, mas estamos falando sobre o ponto em que você bate milhões de notificações. Esta abordagem é um meio-termo agradável que é facilmente extensível.

gem detalhada

Eu construí uma jóia sobre a sua solução: https://github.com/czaks/detailed. Através dela você pode simplificar sua classe Notificação:

class Notification < ActiveRecord::Base
  include Detailed
end

O resto segue o caminho anterior.

Como um bônus adicionado, você pode agora acesso (leitura, escrita, referem) atribui o específico da subclasse diretamente: notification.phone_number, sem recorrer a: notification.details.phone_number. Você também pode escrever todo o código nas principais classes e subclasses, deixando o modelo Detalhes esvaziar. Você também será capaz de fazer menos consultas (no exemplo acima, 4 em vez de N + 1) em grandes conjuntos de dados usando Notification.all_with_details vez do Notification.all regular.

Esteja ciente, que, no momento atual esta jóia não é testado muito bem, embora ele funciona na minha usecase.

Eu sei que isso é velho, mas depois de ter chegado a uma solução que vejo potenciais respostas que poderia usá-lo em todos os lugares! Recentemente bifurcada um projeto promissor para implementar herança múltipla mesa e herança de classe em Rails. Eu passei alguns dias submetendo-a a um desenvolvimento rápido, correções, comentários e documentação e ter re-liberado lo como CITIER Class Herança e herança de tabela embeddings para Rails.

Eu acho que deveria permitir que você faça o que você precisava simplesmente construir os modelos onde Twitter, e-mail e herdar SMS de Notificação. Então, a migração para notificações incluir apenas atributos comuns, e aqueles para os três subtipos incluem seus atributos únicos.

Ou até mesmo definir uma função na classe Notificação de raiz e sobrecarregá-lo em subclasses para devolver algo diferente.

considerar dar-lhe uma olhada: https://github.com/PeterHamilton/citier

Eu estou achando tão útil! Eu (por sinal) bem-vinda qualquer ajuda para a comunidade em questões e testes, limpeza de código etc! Eu sei que isso é algo que muitas pessoas gostaria de receber.

Por favor, certifique-se de actualizar regularmente no entanto, porque como eu disse, tem vindo a melhorar / evoluir a cada dia.

has_one :details, :class_name => "#{self.class.name}Detail"

não funciona. self.class.name no contexto de uma definição de classe é 'Class' assim: class_name é sempre 'ClassDetail'

Por isso, deve ser:

has_one :details, :class_name => "#{self.name}Detail"

Mas idéia muito agradável!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top