À medida que aprendo mais e mais sobre OOP e começo a implementar vários padrões de projeto, continuo voltando aos casos em que as pessoas odeiam o Active Record .
Freqüentemente, as pessoas dizem que ele não é bem escalável (citando o Twitter como seu principal exemplo) - mas ninguém realmente explica por que ele não se dimensiona bem; e / ou como alcançar os prós da RA sem os contras (por meio de um padrão semelhante, mas diferente?)
Espero que isso não se transforme em uma guerra santa sobre padrões de design - tudo que eu quero saber é **** especificamente **** o que há de errado com o Active Record.
Se não escalar bem, por que não?
Que outros problemas isso tem?
ruby-on-rails
design-patterns
oop
activerecord
Adam Tuttle
fonte
fonte
Respostas:
Há ActiveRecord the Design Pattern e ActiveRecord the Rails ORM Library , e também há uma tonelada de cópias para .NET e outras linguagens.
Todas essas são coisas diferentes. Eles geralmente seguem esse padrão de design, mas o estendem e modificam de muitas maneiras diferentes, portanto, antes que alguém diga "ActiveRecord é uma merda", ele precisa ser qualificado dizendo "qual ActiveRecord há montes?"
Só estou familiarizado com o ActiveRecord do Rails, vou tentar resolver todas as reclamações que foram levantadas no contexto de usá-lo.
Código:
Isso gera SQL com
LEFT JOIN companies on companies.id = person.company_id
e gera automaticamente objetos da empresa associados para que você possa fazerpeople.first.company
e não precise acessar o banco de dados porque os dados já estão presentes.Código:
Isso é desencorajado porque é feio, mas para os casos em que você simplesmente precisa escrever SQL bruto, isso é feito facilmente.
Código:
Isso selecionará apenas as colunas de nome e ID do banco de dados, todos os outros 'atributos' nos objetos mapeados serão apenas nulos, a menos que você recarregue manualmente esse objeto e assim por diante.
fonte
Sempre achei que ActiveRecord é bom para aplicativos baseados em CRUD rápidos, onde o modelo é relativamente plano (como em, não muitas hierarquias de classes). No entanto, para aplicativos com hierarquias OO complexas, um DataMapper é provavelmente uma solução melhor. Enquanto ActiveRecord assume uma proporção de 1: 1 entre suas tabelas e seus objetos de dados, esse tipo de relacionamento se torna difícil com domínios mais complexos. Em seu livro sobre padrões , Martin Fowler aponta que ActiveRecord tende a quebrar sob condições em que seu modelo é bastante complexo, e sugere um DataMapper como alternativa.
Descobri que isso é verdade na prática. Nos casos em que você tem muita herança em seu domínio, é mais difícil mapear a herança para seu RDBMS do que mapear associações ou composição.
A forma como eu faço isso é ter objetos de "domínio" que são acessados por seus controladores através dessas classes DataMapper (ou "camada de serviço"). Eles não refletem diretamente o banco de dados, mas atuam como sua representação OO para algum objeto do mundo real. Digamos que você tenha uma classe User em seu domínio e precise ter referências a, ou coleções de outros objetos, já carregados quando você recuperar esse objeto User. Os dados podem vir de muitas tabelas diferentes e um padrão ActiveRecord pode tornar isso muito difícil.
Em vez de carregar o objeto User diretamente e acessar dados usando uma API de estilo ActiveRecord, seu código de controlador recupera um objeto User chamando a API do método UserMapper.getUser (), por exemplo. É esse mapeador que é responsável por carregar quaisquer objetos associados de suas respectivas tabelas e retornar o objeto "domínio" do Usuário completo para o chamador.
Essencialmente, você está apenas adicionando outra camada de abstração para tornar o código mais gerenciável. Se suas classes DataMapper contêm SQL customizado bruto ou chamadas para uma API da camada de abstração de dados, ou mesmo acessam um padrão ActiveRecord, não importa realmente para o código do controlador que está recebendo um objeto User bonito e populado.
Enfim, é assim que eu faço.
fonte
Acho que há provavelmente um conjunto de razões muito diferente entre o motivo pelo qual as pessoas estão "odiando" o ActiveRecord e o que está "errado" com ele.
Sobre a questão do ódio, há muito veneno em relação a qualquer coisa relacionada a Rails. Quanto ao que há de errado com ela, é provável que seja como toda tecnologia e haja situações em que é uma boa escolha e situações em que há melhores escolhas. A situação em que você não consegue tirar proveito da maioria dos recursos do Rails ActiveRecord, na minha experiência, é onde o banco de dados está mal estruturado. Se você estiver acessando dados sem chaves primárias, com coisas que violam a primeira forma normal, onde existem muitos procedimentos armazenados necessários para acessar os dados, é melhor usar algo que seja mais como um invólucro SQL. Se o seu banco de dados for relativamente bem estruturado, ActiveRecord permite que você aproveite isso.
Para adicionar ao tema de responder aos comentadores que dizem que as coisas estão difíceis no ActiveRecord com uma réplica de trecho de código
Ao usar a opção de inclusão, ActiveRecord permite que você substitua o comportamento de carregamento lento padrão.
fonte
Minha resposta longa e tardia, nem mesmo completa, mas uma boa explicação POR QUE odeio esse padrão, opiniões e até algumas emoções:
1) versão resumida: Active Record cria uma " camada fina " de " ligação forte " entre o banco de dados e o código do aplicativo. O que não resolve nenhum problema lógico, nenhum problema, nenhum problema. IMHO ele não fornece NENHUM VALOR, exceto algum açúcar sintático para o programador (que pode então usar uma "sintaxe de objeto" para acessar alguns dados, que existem em um banco de dados relacional). O esforço para criar algum conforto para os programadores deve (IMHO ...) melhor ser investido em ferramentas de acesso a banco de dados de baixo nível, por exemplo, algumas variações de simples, fácil, simples
hash_map get_record( string id_value, string table_name, string id_column_name="id" )
métodos e semelhantes (é claro, os conceitos e elegância variam muito com o linguagem utilizada).2) versão longa: em qualquer projeto baseado em banco de dados onde eu tivesse o "controle conceitual" das coisas, evitei o RA, e isso era bom. Eu geralmente construo uma arquitetura em camadas (mais cedo ou mais tarde você divide seu software em camadas, pelo menos em projetos de médio a grande porte):
A1) o próprio banco de dados, tabelas, relações, até mesmo alguma lógica se o SGBD permitir (o MySQL também está crescido agora)
A2) muitas vezes, há mais do que um armazenamento de dados: sistema de arquivos (blobs no banco de dados nem sempre são uma boa decisão ...), sistemas legados (imagine "como" eles serão acessados, muitas variedades possíveis .. mas isso é não é o ponto ...)
B) camada de acesso ao banco de dados (neste nível, métodos de ferramentas, auxiliares para acessar facilmente os dados no banco de dados são muito bem-vindos, mas AR não fornece nenhum valor aqui, exceto algum açúcar sintático)
C) camada de objetos de aplicativo: "objetos de aplicativo" às vezes são linhas simples de uma tabela no banco de dados, mas na maioria das vezes são objetos compostos de qualquer maneira, e têm alguma lógica superior anexada, então investir tempo em objetos AR neste nível é simplesmente inútil , uma perda de tempo precioso dos codificadores, porque o "valor real", a "lógica superior" desses objetos precisa ser implementada em cima dos objetos AR, de qualquer maneira - com e sem AR! E, por exemplo, por que você deseja ter uma abstração de "Objetos de entrada de log"? O código lógico do aplicativo os grava, mas deve ter a capacidade de atualizá-los ou excluí-los? parece bobo, e
App::Log("I am a log message")
algumas magnitudes são mais fáceis de usar do quele=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert();
. E por exemplo: usar um "objeto de entrada de registro" na visualização de registro em seu aplicativo funcionará para 100, 1000 ou mesmo 10.000 linhas de registro, mas mais cedo ou mais tarde você terá que otimizar - e aposto que na maioria dos casos, você apenas use aquela pequena e bonita instrução SQL SELECT na lógica do seu aplicativo (que quebra totalmente a ideia de AR ...), em vez de embrulhar essa pequena instrução em rígidos quadros de ideias de AR fixos com muito código embrulhando e escondendo-o. O tempo que você perdeu escrevendo e / ou construindo código AR poderia ter sido investido em uma interface muito mais inteligente para ler listas de entradas de log (de muitas maneiras, o céu é o limite). Os codificadores devem ousar inventar novas abstrações para realizar a lógica de sua aplicação que se encaixe na aplicação pretendida e não reimplementar estupidamente padrões bobos, que pareça bom à primeira vista!D) a lógica do aplicativo - implementa a lógica de objetos de interação e criação, exclusão e listagem (!) De objetos lógicos do aplicativo (NÃO, essas tarefas raramente devem ser ancoradas nos próprios objetos lógicos do aplicativo: a folha de papel em sua mesa diz você os nomes e localizações de todas as outras planilhas em seu escritório? esqueça os métodos "estáticos" para listar objetos, isso é bobo, um mau compromisso criado para fazer a maneira humana de pensar se encaixar em [alguns-não-todos-AR-framework-like -] AR pensando)
E) a interface do usuário - bem, o que vou escrever nas linhas a seguir é muito, muito, muito subjetivo, mas, na minha experiência, os projetos que se baseiam em AR muitas vezes negligenciam a parte da interface do usuário de um aplicativo - tempo foi perdido na criação de abstrações obscuras . No final, tais aplicativos desperdiçaram muito tempo dos programadores e parecem aplicativos de programadores para programadores, inclinados à tecnologia por dentro e por fora. Os codificadores se sentem bem (trabalho duro enfim feito, tudo acabado e correto, de acordo com o conceito no papel ...), e os clientes "só tem que aprender que precisa ser assim", porque isso é "profissional" .. ok, desculpe, estou divagando ;-)
Bem, admito que tudo isso é subjetivo, mas é minha experiência (Ruby on Rails excluído, pode ser diferente, e eu não tenho nenhuma experiência prática com essa abordagem).
Em projetos pagos, muitas vezes ouvi a demanda para começar com a criação de alguns objetos de "registro ativo" como um bloco de construção para a lógica de aplicativo de nível superior. Na minha experiência, com frequênciaera uma espécie de desculpa para o cliente (uma empresa de desenvolvimento de software na maioria dos casos) não ter um bom conceito, uma visão ampla, uma visão geral do que o produto deveria ser. Esses clientes pensam em quadros rígidos ("no projeto há dez anos funcionava bem ..."), eles podem dar corpo a entidades, podem definir relações de entidades, podem quebrar relações de dados e definir lógicas básicas de aplicação, mas então param e entregá-lo a você, e acho que isso é tudo de que você precisa ... muitas vezes falta-lhes um conceito completo de lógica de aplicativo, interface de usuário, usabilidade e assim por diante ... eles não têm a visão ampla e não gostam do detalhes, e eles querem que você siga aquele jeito de RA das coisas, porque ... bem, por que funcionou naquele projeto anos atrás, mantém as pessoas ocupadas e em silêncio? Eu não sei. Mas os "detalhes" separar os homens dos meninos, ou ... como era o slogan do anúncio original? ;-)
Depois de muitos anos (dez anos de experiência em desenvolvimento ativo), sempre que um cliente menciona um "padrão de registro ativo", meu alarme toca. Eu aprendi a tentar levá-los de volta à fase essencial da concepção , deixá-los pensar duas vezes, tentar mostrar suas fraquezas conceituais ou simplesmente evitá-los se eles não forem discernentes (no final, você sabe, um cliente que ainda não sabe sabe o que quer, talvez até pense que sabe, mas não sabe, ou tenta externalizar o conceito de trabalho para MIM de graça, me custa muitas horas, dias, semanas e meses preciosos do meu tempo, a vida é muito curta ...).
Então, finalmente: ISTO é porque eu odeio aquele "padrão de registro ativo" idiota, e eu odeio e evitarei isso sempre que possível.
EDIT : Eu até chamaria isso de No-Pattern. Não resolve nenhum problema (os padrões não têm como objetivo criar açúcar sintático). Isso cria muitos problemas: a raiz de todos os seus problemas (mencionados em muitas respostas aqui ..) é que ele apenas esconde o bom e velho SQL bem desenvolvido e poderoso por trás de uma interface que é pela definição de padrões extremamente limitada.
Este padrão substitui a flexibilidade por açúcar sintático!
Pense nisso, qual problema o RA resolve para você?
fonte
before_save
retornos de chamada sensíveis para manter a consistência no registro 4)after_commit
ganchos para acionadores de serviço externo. 5) Um bom DSL para organizar mudanças DDL em changesets (migrações). (Ainda há dor lá, mas não há padrão é pior quando> 1 desenvolvedor.)Algumas mensagens estão me deixando confuso. Algumas respostas vão para "ORM" vs "SQL" ou algo parecido.
O fato é que AR é apenas um padrão de programação de simplificação, onde você tira proveito de seus objetos de domínio para escrever o código de acesso ao banco de dados.
Esses objetos geralmente têm atributos de negócios (propriedades do bean) e alguns comportamentos (métodos que geralmente funcionam nessas propriedades).
O AR apenas diz "adicione alguns métodos a esses objetos de domínio" para tarefas relacionadas ao banco de dados.
E devo dizer, pela minha opinião e experiência, que não gosto do padrão.
À primeira vista, pode parecer muito bom. Algumas ferramentas Java modernas, como Spring Roo, usam esse padrão.
Para mim, o verdadeiro problema é apenas com a preocupação OOP. O padrão AR força você de alguma forma a adicionar uma dependência de seu objeto aos objetos de infraestrutura. Esses objetos de infra-estrutura permitem ao objeto de domínio consultar a base de dados através dos métodos sugeridos pela AR.
Sempre disse que duas camadas são a chave para o sucesso de um projeto. A camada de serviço (onde reside a lógica do negócio ou pode ser exportada por meio de algum tipo de tecnologia de remoting, como Web Services, por exemplo) e a camada de domínio. Na minha opinião, se adicionarmos algumas dependências (não realmente necessárias) aos objetos da camada de domínio para resolver o padrão AR, nossos objetos de domínio serão mais difíceis de compartilhar com outras camadas ou (raros) aplicativos externos.
A implementação de AR do Spring Roo é interessante, porque não depende do objeto em si, mas de alguns arquivos AspectJ. Mas se mais tarde você não quiser trabalhar com o Roo e precisar refatorar o projeto, os métodos AR serão implementados diretamente nos seus objetos de domínio.
Outro ponto de vista. Imagine que não usamos um banco de dados relacional para armazenar nossos objetos. Imagine que a aplicação armazene nossos objetos de domínio em um Banco de Dados NoSQL ou apenas em arquivos XML, por exemplo. Implementaríamos os métodos que realizam essas tarefas em nossos objetos de domínio? Acho que não (por exemplo, no caso do XM, adicionaríamos dependências relacionadas a XML aos nossos objetos de domínio ... Realmente triste, eu acho). Por que então temos que implementar os métodos de banco de dados relacionais nos objetos de domínio, como diz o padrão Ar?
Resumindo, o padrão AR pode parecer mais simples e bom para aplicações pequenas e simples. Mas, quando temos aplicativos complexos e grandes, acho que a arquitetura clássica em camadas é uma abordagem melhor.
fonte
A pergunta original está marcada com rails e se refere ao Twitter, que é construído em Ruby on Rails. O framework ActiveRecord dentro do Rails é uma implementação do padrão de projeto Active Record de Fowler.
fonte
A principal coisa que vi com relação às reclamações sobre o Active Record é que quando você cria um modelo em torno de uma mesa e seleciona várias instâncias do modelo, basicamente está fazendo um "selecionar * de ...". Isso é bom para editar um registro ou exibir um registro, mas se você quiser, digamos, exibir uma lista das cidades para todos os contatos em seu banco de dados, você pode "selecionar cidade de ..." e obter apenas as cidades . Fazer isso com o Active Record exigiria que você selecione todas as colunas, mas apenas usando City.
Claro, várias implementações irão lidar com isso de forma diferente. No entanto, é um problema.
Agora, você pode contornar isso criando um novo modelo para a coisa específica que você está tentando fazer, mas algumas pessoas argumentariam que é mais esforço do que benefício.
Eu, eu curto o Active Record. :-)
HTH
fonte
Adoro a forma como o SubSonic faz apenas uma coluna.
Ou
, ou:
Mas Linq ainda é rei quando se trata de carregamento preguiçoso.
fonte
@BlaM: Às vezes, acabei de implementar um registro ativo para o resultado de uma junção. Nem sempre tem que ser a relação Tabela <--> Active Record. Por que não "Result of a Join statement" <--> Active Record?
fonte
Vou falar sobre Active Record como padrão de design, não vi ROR.
Alguns desenvolvedores odeiam o Active Record, porque lêem livros inteligentes sobre como escrever código limpo e organizado, e esses livros afirmam que o registro ativo viola o princípio de responsabilidade único, viola a regra DDD de que o objeto de domínio deve ser persistente ignorante e muitas outras regras desse tipo de livro .
A segunda coisa que os objetos de domínio no Active Record tendem a ser 1 para 1 com o banco de dados, o que pode ser considerado uma limitação em alguns tipos de sistemas (principalmente n-tier).
Isso é apenas coisas abstratas, eu não vi a implementação real deste padrão do Ruby on Rails.
fonte
O problema que vejo com Active Records é que sempre se trata de apenas uma tabela. Tudo bem, contanto que você realmente trabalhe apenas com aquela tabela, mas quando você trabalha com dados na maioria dos casos, você terá algum tipo de junção em algum lugar.
Sim, a junção geralmente é pior do que nenhuma junção quando se trata de desempenho, mas a junção geralmente é melhor do que a junção "falsa" , primeiro lendo toda a tabela A e, em seguida, usando as informações obtidas para ler e filtrar a tabela B.
fonte
O problema com o ActiveRecord é que as consultas que ele gera automaticamente para você podem causar problemas de desempenho.
Você acaba fazendo alguns truques não intuitivos para otimizar as consultas que o deixam se perguntando se teria sido mais eficaz escrever a consulta à mão em primeiro lugar.
fonte
Embora todos os outros comentários sobre a otimização de SQL sejam certamente válidos, minha principal reclamação com o padrão de registro ativo é que geralmente leva a incompatibilidade de impedância . Gosto de manter meu domínio limpo e devidamente encapsulado, o que o padrão de registro ativo geralmente destrói toda esperança de fazer.
fonte
Tente fazer um relacionamento polimórfico de muitos para muitos. Não tão fácil. Especialmente quando você não está usando o STI.
fonte