Como sugerir o uso de um ORM em vez de procedimentos armazenados?

31

Eu trabalho em uma empresa que usa apenas procedimentos armazenados para todo o acesso a dados, o que torna muito irritante manter nossos bancos de dados locais sincronizados, a cada confirmação que temos para executar novos procs. Eu usei algumas ORMs básicas no passado e acho a experiência muito melhor e mais limpa. Gostaria de sugerir ao gerente de desenvolvimento e ao restante da equipe que analisemos o uso de um ORM de algum tipo para desenvolvimento futuro (o restante da equipe conhece apenas os procedimentos armazenados e nunca usou mais nada). A arquitetura atual é o .NET 3.5, escrito como o .NET 1.1, com "classes god" que usam uma implementação estranha do ActiveRecord e retornam DataSets não digitados que são repetidos em arquivos code-behind - as classes funcionam da seguinte forma:

class Foo { 
    public bool LoadFoo() { 
        bool blnResult = false;
        if (this.FooID == 0) { 
            throw new Exception("FooID must be set before calling this method.");
        }

        DataSet ds = // ... call to Sproc
        if (ds.Tables[0].Rows.Count > 0) { 
            foo.FooName = ds.Tables[0].Rows[0]["FooName"].ToString();
            // other properties set
            blnResult = true;
        }
        return blnResult;
    }
}

// Consumer
Foo foo = new Foo();
foo.FooID = 1234;
foo.LoadFoo();
// do stuff with foo...

Não há praticamente nenhuma aplicação de padrões de design. Não há nenhum teste (ninguém mais sabe como escrever testes de unidade, e o teste é feito carregando manualmente o site e bisbilhotando). Examinando nosso banco de dados, temos: 199 tabelas, 13 visualizações, 926 procedimentos armazenados e 93 funções. Cerca de 30 tabelas são usadas para tarefas em lote ou externas, e as demais são usadas em nosso aplicativo principal.

Vale a pena seguir uma abordagem diferente nesse cenário? Estou falando de avançar apenas porque não temos permissão para refatorar o código existente, pois "funciona", portanto não podemos alterar as classes existentes para usar um ORM, mas não sei com que frequência adicionamos novos módulos. de adicionar / corrigir módulos atuais, portanto, não tenho certeza se um ORM é a abordagem correta (muito investido em procedimentos armazenados e DataSets). Se for a escolha certa, como devo apresentar o caso para usar uma? Em primeiro lugar, os únicos benefícios que consigo pensar são em ter um código mais limpo (embora possa não ser, pois a arquitetura atual não é " t construído com ORMs em mente, para que basicamente integrássemos os ORMs para módulos futuros, mas os antigos ainda usariam os DataSets) e menos problemas para lembrar quais scripts de procedimento foram executados e quais precisam ser executados, etc, mas é isso, e eu não sei o quão convincente seria um argumento. A manutenção é outra preocupação, mas que ninguém, exceto eu, parece se preocupar.

Wayne Molina
fonte
8
Parece que você tem mais problemas do que simplesmente convencer a equipe a usar ORMs. Parece-me que sua equipe não está ciente de algumas boas práticas de desenvolvimento (por exemplo, padrões de design, testes de unidade). Essas são questões mais importantes que você precisa resolver.
11113 Bernard
6
Ironicamente, acho que em cerca de 5 anos de desenvolvimento, conheci apenas algumas pessoas / equipes que estavam cientes de coisas como padrões de design e testes de unidade; geralmente sou o único cara na empresa que sabe sobre essas coisas.
Wayne Molina
3
@Wayne M: Acho isso um pouco perturbador, mas também não estou surpreso com isso.
11113 Bernard
2
Eu achei isso muito ... desanimador. É estranho quando você sugere algo e tem uma aparência de "veado nos faróis" que indica que a outra pessoa não tem a menor idéia do que você está falando ou por que alguém consideraria fazer isso. Isso já aconteceu várias vezes no passado.
Wayne Molina
2
Sou um grande fã do Stored Procedure, então meu comentário é tendencioso, mas discordo completamente de toda a premissa. Você gosta de ORM e deseja usar isso bem. No entanto, o resto da equipe está bem com os procs armazenados. Por que forçá-los ao que você gosta?
Darknight

Respostas:

47

Os procedimentos armazenados são ruins, geralmente são lentos e aproximadamente tão eficientes quanto o código comum do lado do cliente.

[A aceleração geralmente ocorre devido à maneira como o cliente e a interface do procedimento armazenado são projetados e a maneira como as transações são gravadas como breves e focadas explosões de SQL.]

Os procedimentos armazenados são um dos piores lugares para colocar código. Ele divide seu aplicativo em dois idiomas e plataformas, de acordo com regras geralmente aleatórias.

[Esta questão será rebaixada para ter uma pontuação de cerca de -30 porque muitas, muitas pessoas acham que os procedimentos armazenados têm poderes mágicos e devem ser usados ​​apesar dos problemas que causam.]

Mover todo o código do procedimento armazenado para o cliente facilitará as coisas para todos.

Você ainda precisará atualizar o esquema e o modelo ORM de tempos em tempos. No entanto, as alterações de esquema são isoladas das alterações do ORM, permitindo certa independência entre aplicativos e o esquema do banco de dados.

Você poderá testar, corrigir, manter, entender e adaptar todos os procedimentos armazenados à medida que os reescrever. Seu aplicativo será executado da mesma forma e se tornará muito menos frágil porque você não está mais invadindo duas tecnologias diferentes.

ORMs não são mágicos, e boas habilidades de design de banco de dados são absolutamente essenciais para fazê-lo funcionar.

Além disso, os programas com muito SQL do cliente podem ficar lentos devido ao mau pensamento sobre os limites da transação. Um dos motivos pelos quais os procedimentos armazenados parecem ser rápidos é que os procedimentos armazenados forçam um design muito, muito cuidadoso das transações.

ORMs não forçam magicamente o design cuidadoso da transação. O design da transação ainda precisa ser feito com o mesmo cuidado que se fazia ao escrever procedimentos armazenados.

S.Lott
fonte
19
+1 porque os procedimentos armazenados são uma dor total para se trabalhar #
Gary Rowe
3
: '(Não tenho nenhum problema com procedimentos armazenados É apenas uma outra camada de abstração para mim..
Mike Weller
3
Se criarmos o SP, o mecanismo de banco de dados o armazenará como forma compilada e ele criará o caminho de execução para que seja executado o mais rápido possível. Porém, o ORM envia SQL sempre que precisa ser compilado e executado pelo mecanismo de banco de dados. Eu acho que será mais lento usar um ORM em vez de procedimento armazenado.
DeveloperArnab
4
Engraçado. Voltamos de um ORM para procedimentos armazenados apenas por causa de .. SPEED. Não é a quantidade de tempo que precisamos para programar, mas o tempo que um ORM precisa para coisas como materializar objetos, procurar um objeto, atualizar em clientes diferentes. SP, onde muito mais rápido. Um exemplo: ler 30.000 objetos do DB com um novo ORM moderno precisa ... tudo bem. tempo limite após 2 minutos. Chamando o procedimento armazenado e obtendo o resultado - 2 segundos. Sim - existem muitos truques como paginação para reduzir callign a muito de um DB - mas grande diferença se eles são apenas que um ORM pode ser usado ou para
Offler
2
@DeveloperArnab: Isso pode ter sido verdade há 20 anos, mas os mecanismos modernos de banco de dados são bastante sofisticados e podem reconhecer consultas executadas anteriormente e reutilizar planos de execução, a diferença de velocidade hoje em dia é tão pequena que dificilmente justifica o incômodo adicional dos SPs.
Whatsisname
20

Os procedimentos armazenados são bons, rápidos e muito eficientes e são o local ideal para colocar seu código relacionado a dados. Mover todo esse código para o cliente tornará as coisas um pouco mais fáceis para você como desenvolvedor do cliente (um pouco, pois você ainda precisará atualizar o esquema e o modelo ORM ao confirmar a alteração), mas perderá todo o código existente e fará seu aplicativo mais lento e provavelmente mais frágil, considerando a perda de todas essas habilidades sql.

Gostaria de saber se os DBAs estão sentados lá dizendo "oh, toda confirmação, eu tenho que desligar o cliente novamente, devemos mover todo o código para os formulários de banco de dados".

No seu caso, você poderá substituir o ORM personalizado existente (ou seja, suas classes engraçadas) por outra pessoa sem nenhuma perda, exceto alterações na maneira como seu código code-behind é escrito. Você também pode manter os SPs, pois a maioria dos ORMs (todos?) Os chamará com prazer. Portanto, eu recomendaria substituir as classes "Foo" por um ORM e partir daí. Eu não recomendaria substituir seus SPs.

PS. Parece que você tem muito código comum nas classes code-behind, o que as torna um padrão de design em si mesmas. o que você acha que é um padrão de design em primeiro lugar! (ok, pode não ser o melhor, ou até bom, mas ainda assim é um DP)

Edit: e agora com o Dapper , qualquer motivo para evitar brigas por um ORM pesado se foi.

gbjbaanb
fonte
3
+1, o primeiro passo óbvio é mover para um ORM e usá-lo para mapear os resultados dos procedimentos armazenados existentes para objetos. Livre-se de toda essa ds.Tables[0].Rows[0]["FooName"].ToString()porcaria. Manager ama procedimentos armazenados? Ele fica com eles. Mas seria fisicamente impossível argumentar que mover todo esse código repetitivo de clichê para algo gerado por, digamos, LINQ to SQL, era uma coisa ruim.
Carson63000 11/11
11
Bem, eu não posso acreditar o quanto "errado" há no seu post. Sua comparação com um DBA queixando-se de ter que puxar o código é inapropriada e completamente sem sentido. Primeiro de tudo, o banco de dados é um SERVIÇO destinado a armazenar e recuperar dados, no final da história. LOGIC, como o nome indica, entra na Business Logic Layer, que faz parte do código da API. App mais frágil se afastando de sprocs? Você está falando sério ou apenas trollando? TDD um aplicativo com lógica em sprocs e me diga o quanto isso é divertido. ORMs também não devem ser trocados. Os bancos de dados são, pois são apenas um SERVIÇO.
Matteo Mosca
5
Eu realmente não consigo entender por que algumas pessoas pensam que o banco de dados é algum tipo de terra santa, onde tudo é sagrado. Pense sobre isso. Quando uma empresa terceirizada expõe um serviço para você, eles fornecem acesso direto ao banco de dados ou o ocultam atrás de uma API? Para usar um exemplo popular, use qualquer serviço do Google. Você, desenvolvedor, conecta-se à API pública deles, nem sabe qual banco de dados está nele ou se existe algum banco de dados. É assim que você cria um software sólido e dissociado. Os bancos de dados não devem ser acessados ​​diretamente pelos aplicativos. As camadas intermediárias existem para isso.
Matteo Mosca
5
@ Matteo Mosca: claro, mas como o middleware acessa o DB ... é um cliente para o DB. "cliente" não significa "GUI" ou "aplicativo de desktop". Há muitas áreas cinzentas nas camadas de aplicativos - você coloca a validação na GUI ou na lógica de servidor / negócios? O servidor deve ter um design limpo, mas isso será muito ruim para desempenho e capacidade de resposta do usuário. Da mesma forma, você costuma colocar alguma lógica no banco de dados quando melhora o desempenho e (nesse caso) a correção dos dados.
precisa saber é
4
Sinto muito, mas ainda discordo. No dia em que você precisa implantar seu aplicativo em um ambiente diferente, onde não possui a mesma tecnologia de banco de dados usada, você se encontra em uma situação em que seu aplicativo não pode ser executado porque não possui todos esses sprocs no novo banco de dados .. e mesmo que você os recrie (o que é uma dor total), eles podem ser diferentes, devido às diferenças de dialetos SQL, etc. Uma API centralizada com lógica e validação, exposta através de um padrão (como SOAP ou REST), resolve isso problema e adiciona testabilidade, controle de versão e consistência.
Matteo Mosca
13

Você parece estar tentando liderar sua equipe de um extremo (procedimentos armazenados e conjuntos de dados) para outro (um ORM completo). Acho que existem outras mudanças mais incrementais que você pode definir sobre a implementação para melhorar a qualidade do código da camada de acesso a dados, que sua equipe pode estar mais disposta a aceitar.

O código de implementação de registro ativo incompleto que você postou não é particularmente elegante - eu recomendo pesquisar o Padrão de Repositório, que é fácil de entender e implementar, e é muito popular entre os desenvolvedores .NET. Esse padrão é frequentemente associado aos ORMs, mas é igualmente fácil criar repositórios usando o ADO.NET simples.

Quanto aos DataSet - eca! Suas bibliotecas de classes serão muito mais fáceis de trabalhar se você retornar objetos digitados estaticamente (ou mesmo dinâmicos). Acredito que esta ilustração explica melhor minha opinião sobre o DataSet.

Além disso, você pode abandonar os procs armazenados sem pular para um ORM - não há nada errado em usar SQL parametrizado. Na verdade, eu definitivamente preferiria usar os proc armazenados, a menos que você tenha procedimentos complexos que economizem em várias viagens de ida e volta ao servidor. Eu também odeio quando abro um banco de dados legado e vejo uma lista interminável de procedimentos CRUD.

Não estou desencorajando o uso de ORMs - geralmente os uso na maioria dos projetos em que trabalho. No entanto, posso ver por que pode haver bastante atrito ao tentar introduzir um neste projeto e sua equipe, para colocar gentilmente, parece que eles pararam de aprender coisas novas há cerca de 8 anos. Dito isto, eu definitivamente daria uma olhada na nova geração de "Micro ORM's", como o Dapper (usado para alimentar este site nem menos) e o Massive , que são incrivelmente fáceis de usar e o mantêm mais próximo do SQL do que um ORM típico, e que sua equipe pode estar mais disposta a aceitar.

richeym
fonte
2
Acho que o problema foi justamente quando o aplicativo foi escrito, não havia ORMs reais para .NET, então foi feito de outra maneira (da maneira ASP.NET 1.1) e ninguém nunca pensou em fazer isso (ou qualquer outra coisa) de maneira diferente até eu me juntar a alguns meses atrás.
Wayne Molina
1
+1 para Dapper. Comecei a usá-lo em um projeto e é extremamente fácil de implementar e usar. Eu já sou um grande fã.
SLoret
4

Estou em uma situação semelhante, na verdade nosso hardware, poder e política se inclinam para o lado do banco de dados, então tudo passa por um procedimento armazenado. Infelizmente, eles são um problema para um codificador, especialmente quando se trata de metadados e geração de código, pois não há metadados tão ricos em procedimentos armazenados como tabelas.

Independentemente disso, você ainda pode escrever um código elegante e limpo usando procs armazenados. Atualmente, estou implementando o padrão de repositório com todos os procedimentos armazenados. Sugiro que você procure no FluentAdo.net sua brilhante idéia ao mapear do datareader de volta para seus objetos de negócios. Peguei uma parte dessa ideia e a adaptei novamente para minha solução doméstica.

Justin
fonte
3

JFTR - Sou um desenvolvedor humilde de PHP, mas isso parece uma questão predominantemente política.

Dada a escala da "podridão" que sustenta o aplicativo, então - práticas recomendadas de lado - haveria uma sobrecarga significativa para eliminá-lo. Parece que está na fronteira com a reescrita do território.

Você pode garantir que a alternativa que você sugere traria benefícios para justificar o custo para os negócios? Eu suspeito que o ROI deste empreendimento possa ser difícil de vender para a empresa. A menos que o aplicativo seja instável ou você possa provar o mérito da revisão em termos financeiros - isso pode ser difícil.

O ORM é a única alternativa ao SPROCS? Existem alguns padrões de design entre um ORM completo e o SQL vanilla. Talvez você possa iniciar o processo trazendo esses SPROCS gradualmente para fora do DB em um DBAL. É claro que existe o perigo de que isso se torne um ORM de homebrew ao longo do tempo - mas você teria um passo mais perto do objetivo.

sunwukung
fonte
2

Mudamos de SP para ORM alguns anos atrás.

Em um caso, tivemos que atualizar 80 tabelas. O antigo modelo de estimativa teria estimado 80 horas para isso com entlib e SP. Fizemos isso em 10 :)

Isso nos proporcionou uma redução de 80% na quantidade de tempo que usamos para desenvolver a camada de acesso a dados.

Shiraz Bhaiji
fonte
1
E você não conseguiu criar um script da atualização da tabela, por quê?
Darknight
Isso estava pegando um objeto complexo na memória e salvando-o em um banco de dados relacional para geração de relatórios. Escrevemos um programa que refletia sobre o objeto para criar a estrutura da tabela.
Shiraz Bhaiji
-1

Parece-me mais que o problema subjacente é que suas implantações não estão funcionando corretamente e automaticamente.

Pode ser mais fácil configurar um mecanismo de compilação contínuo que saiba como manipular os bancos de dados conforme necessário após cada confirmação.


fonte