Quanta lógica de negócios o banco de dados deve implementar?


107

Trabalhei em alguns projetos em que a maior parte da lógica de negócios foi implementada no banco de dados (principalmente por meio de procedimentos armazenados). Por outro lado, ouvi de alguns colegas programadores que essa é uma prática ruim ("Os bancos de dados existem para armazenar dados. Os aplicativos existem para fazer o resto").

Qual dessas abordagens é geralmente a melhor?

Os profissionais da implementação da lógica de negócios no banco de dados em que posso pensar são:

  • Centralização da lógica de negócios;
  • Independência do tipo de aplicativo, linguagem de programação, SO, etc;
  • Os bancos de dados são menos propensos a migração de tecnologia ou grandes refatorações (AFAIK);
  • Sem retrabalho na migração da tecnologia de aplicativos (por exemplo: .NET para Java, Perl para Python, etc).

Os contras:

  • O SQL é menos produtivo e mais complexo para a programação da lógica de negócios, devido à falta de bibliotecas e construções de linguagem que as linguagens mais orientadas a aplicativos oferecem;
  • Reutilização de código mais difícil (se possível) através das bibliotecas;
  • IDEs menos produtivos.

Nota: Os bancos de dados de que falo são bancos de dados relacionais e populares, como SQL Server, Oracle, MySql etc.

Obrigado!


3
Você pode encontrar a resposta para esta pergunta útil.
Blrfl

7
Este argumento já foi debatido exaustivamente . O que mais poderíamos adicionar significativamente à conversa aqui?
9788 Robert Harvey

2
@gnat: Nem mesmo perto.
9788 Robert Harvey


7
Considere que o banco de dados está indo muito longe ( longe ) do seu aplicativo. O banco de dados pode até sobreviver ao idioma em que você escreve seu aplicativo. Os dados em si costumam ser os negócios e o banco de dados deve poder proteger a integridade dos dados que ele contém. Nesse sentido, toda restrição de chave estrangeira é, francamente, a implementação de uma regra de negócios. A menos que você se livre de todas as restrições relacionais em seu banco de dados relacional, você realmente não pode obter completamente a lógica comercial do banco de dados.
Craig

Respostas:


82

A lógica de negócios não entra no banco de dados

Se estamos falando de aplicativos de várias camadas, parece bastante claro que a lógica de negócios, o tipo de inteligência que administra uma empresa específica, pertence à camada lógica de negócios, não à camada de acesso a dados.

Os bancos de dados fazem algumas coisas muito bem:

  1. Eles armazenam e recuperam dados
  2. Eles estabelecem e reforçam relacionamentos entre diferentes entidades de dados
  3. Eles fornecem os meios para consultar os dados em busca de respostas
  4. Eles fornecem otimizações de desempenho.
  5. Eles fornecem controle de acesso

Agora, é claro, você pode codificar todos os tipos de coisas em um banco de dados que pertencem às suas preocupações comerciais, como taxas de impostos, descontos, códigos de operação, categorias e assim por diante. Mas a ação comercial executada nesses dados geralmente não é codificada no banco de dados, por todos os tipos de razões já mencionados por outros, embora uma ação possa ser escolhida no banco de dados e executada em outro local.

E, é claro, pode haver coisas que são executadas em um banco de dados por desempenho e outros motivos:

  1. Encerramento de um período contábil
  2. Trituração de números
  3. Processos noturnos em lote
  4. Fail-over

Naturalmente, nada está gravado em pedra. Os procedimentos armazenados são adequados para uma ampla variedade de tarefas, simplesmente porque residem no servidor de banco de dados e possuem certos pontos fortes e vantagens.

Procedimentos armazenados em todos os lugares

Existe um certo fascínio em codificar todas as suas tarefas de armazenamento, gerenciamento e recuperação de dados em procedimentos armazenados e simplesmente consumir os serviços de dados resultantes. Você certamente se beneficiaria com o máximo possível de otimizações de desempenho e segurança que o servidor de banco de dados poderia fornecer, e isso não é pouca coisa.

Mas o que você arrisca?

  1. Bloqueio do fornecedor
  2. A necessidade de desenvolvedores com conjuntos de habilidades especiais
  3. Ferramentas de programação espartana, em geral
  4. Acoplamento de software extremamente rígido
  5. Sem separação de preocupações

E, claro, se você precisar de um serviço da Web (que provavelmente é onde tudo está indo), você ainda precisará criar isso.

Então, o que é prática típica?

Eu diria que uma abordagem moderna e típica é usar um Mapeador Objeto-Relacional (como o Entity Framework) para criar classes que modelam suas tabelas. Você pode falar com seu banco de dados através de um repositório que retorna coleções de objetos, uma situação que é muito familiar para qualquer desenvolvedor de software competente. O ORM gera dinamicamente SQL correspondente ao seu modelo de dados e às informações solicitadas, que o servidor de banco de dados processa para retornar os resultados da consulta.

Quão bem isso funciona? Muito bem e muito mais rapidamente do que escrever procedimentos e visualizações armazenados. Isso geralmente cobre cerca de 80% dos requisitos de acesso a dados, principalmente CRUD. O que cobre os outros 20%? Você adivinhou: procedimentos armazenados, que todos os principais ORMs suportam diretamente.

Você pode escrever um gerador de código que faça o mesmo que um ORM, mas com procedimentos armazenados? Certamente você pode. Mas as ORMs geralmente são independentes do fornecedor, bem compreendidas por todos e têm melhor suporte.


3
Obrigado pela sua ótima resposta, @Robert Harvey. Mas eu estava pensando no argumento "bloqueio de fornecedor": não usar uma tecnologia específica (por exemplo, a pilha .NET ou Java) para criar um aplicativo e também um bloqueio de fornecedor? Ou há vantagens de um bloqueio de fornecedor de pilha orientado a aplicativos em comparação com um de banco de dados?
Raphael

3
@RobertHarvey, mas a parte da lógica do aplicativo que está no .NET ainda está bloqueada no .NET. O mesmo vale para PHP e Java.
Pacerier 07/12/14

2
@ Pacerier: Por vendedor-lockin, estou me referindo ao fornecedor de banco de dados. Na prática real, o banco de dados (e a pilha de programação) raramente são substituídos.
Robert Harvey

2
@kai: Bem, você não pode ter as duas coisas. Ou você usa stubs e zombarias e vive com o fato de que o teste é artificial, ou você escreve um teste realista e vive com um pouco de atraso. Duvido que sua troca seja de 10 minutos vs. 30 segundos.
Robert Harvey

3
Talvez esteja atrasado, mas sou da opinião de que os procedimentos armazenados que implementam a lógica de negócios pertencem à camada de lógica de negócios, não à camada de dados. Eles são uma espécie de linguagem separada, sem necessidade de ORM.
Paralife 04/04

16

Acredito firmemente em manter a lógica de negócios fora do banco de dados, tanto quanto possível. No entanto, como desenvolvedor de desempenho da minha empresa, compreendo que às vezes seja necessário obter um bom desempenho. Mas acho que é necessário com muito menos frequência do que as pessoas afirmam.

Eu discuto seus prós e contras.

Você alega que centraliza sua lógica de negócios. Pelo contrário, acho que o descentraliza. Em um produto em que trabalho atualmente, usamos o procedimento armazenado para grande parte da nossa lógica de negócios. Muitos de nossos problemas de desempenho vêm da chamada de funções repetidamente. Por exemplo

select <whatever>
from group g
where fn_invoker_has_access_to_group(g.group_id)

O problema dessa abordagem é que geralmente (em alguns casos, isso é falso) força o banco de dados a executar sua função N vezes, uma vez por linha. Às vezes, essa função é cara. Alguns bancos de dados suportam índices de função. Mas você não pode indexar todas as funções possíveis em relação a todas as entradas possíveis. Ou você pode?

Uma solução comum para o problema acima é extrair a lógica da função e mesclá-la na consulta. Agora você quebrou o encapsulamento e a lógica duplicada.

Outro problema que vejo é chamar procedimentos armazenados em um loop, porque não há como ingressar ou cruzar conjuntos de resultados de processos armazenados.

declare some_cursor
while some_cursor has rows
    exec some_other_proc
end

Se você extrair o código do processo aninhado, descentralizará novamente. Portanto, você é forçado a escolher entre encapsulamento e desempenho.

Em geral, acho que os bancos de dados são ruins em:

  1. Computação
  2. Iteração (eles são otimizados para operações definidas)
  3. Balanceamento de carga
  4. Análise

Os bancos de dados são bons em:

  1. Bloqueio e desbloqueio
  2. Manutenção de dados e seus relacionamentos
  3. Garantindo a integridade

Ao realizar operações caras, como loops e análise de strings e mantê-las na camada do aplicativo, você pode dimensionar horizontalmente o aplicativo para obter um melhor desempenho. Adicionar vários servidores de aplicativos atrás de um balanceador de carga geralmente é muito mais barato do que configurar a replicação de banco de dados.

Você está correto, no entanto, que desacopla sua lógica de negócios da linguagem de programação de seu aplicativo, mas não vejo por que isso é uma vantagem. Se você possui um aplicativo Java, possui um aplicativo Java. A conversão de vários códigos Java em procedimentos armazenados não altera o fato de você ter um aplicativo Java.

Minha preferência é manter o código do banco de dados focado na persistência. Como você cria um novo widget? Você deve inserir em 3 tabelas e elas devem estar em uma transação. Isso pertence a um procedimento armazenado.

A definição do que pode ser feito com um widget e as regras de negócios para localizar widgets pertencem ao seu aplicativo.


8
No SQL Server, apenas sps mal escritos precisam ser chamados em um loop, você pode enviar conjuntos de dados em um parâmetro e executar um processo baseado em conjunto.
HLGEM

2
O SQL Server gerará um plano de consulta abaixo do ideal sempre que houver uma UDF em uma cláusula WHERE.
Jim G.

7
Parece que o seu problema de desempenho não é culpa da lógica no banco de dados x aplicativo .. é apenas mal escrito e arquitetado. Esse problema o seguirá no mundo ORM da mesma forma. ORMs podem ser uma verdadeira dor de cabeça fora das operações CRUD. Se o seu sistema possui dados pesados, tipo de sistema de relatório, tenha cuidado.
sam yi

Isso é verdade. A maioria dos nossos problemas de desempenho se deve simplesmente ao código mal escrito e à arquitetura excessivamente complexa. Mas ainda acredito que colocamos o tipo errado de trabalho em nossos bancos de dados. Codificar o máximo possível no banco de dados nos levou a fazer coisas nas quais um banco de dados não é bom.
Brandon

1
Este exemplo é até um argumento para colocar partes principais da lógica de negócios no DB: para evitar uma abordagem iterativa (loops de código ou cursor em vez de expressões baseadas em conjuntos) como a praga. Os programadores tendem a tratar conjuntos de objetos de maneira iterativa (loop, transversal), provavelmente levando a cargas desnecessárias ou ao problema SELECT N + 1 de muitas viagens de ida e volta de uma consulta. Usando expressões baseadas em linguagem ou SQL (por exemplo, LINQ), elas serão forçadas a usar uma abordagem baseada em conjunto, sempre que possível.
Erik Hart

10

Trabalhei em 2 empresas diferentes que tinham uma visão diferente sobre o assunto.

Minha sugestão pessoal seria usar procedimentos armazenados quando o tempo de execução for importante (desempenho). Como o Stored Procedure é compilado, se você tiver uma lógica complexa para consultar os dados, é melhor mantê-lo no próprio banco de dados. Além disso, ele enviará apenas os dados finais para o seu programa no final.

Caso contrário, acho que a lógica de um programa deve sempre estar no próprio software. Por quê? Como um programa precisa ser testável e não acho que exista uma maneira fácil de testar o procedimento armazenado da unidade. Não se esqueça, um programa que não foi testado é um programa ruim.

Portanto, use o procedimento armazenado com cuidado, quando necessário.


3
Os procedimentos armazenados são testáveis ​​por unidade. Veja aqui algumas técnicas.
Robert Harvey

4
depois, um teste de unidade nunca usa banco de dados ou arquivo. Portanto, tecnicamente, o "teste de unidade" de um procedimento armazenado não é um teste de unidade e será lento como o inferno. Um conjunto de testes de unidade deve ser executado em segundos (ou talvez minutos com aplicativos muito grandes) a qualquer momento durante o desenvolvimento.
Jean-François Côté

1
O OP estava falando sobre "lógica de negócios" e a lógica de negócios deve ser testada por unidade. Ao colocá-lo em um procedimento armazenado, você o mistura com a consulta ao banco de dados, que atrasa todo o processo. Como eu disse, você pode usar o Stored Procedure (não é crime), mas isso embaçará a linha entre a lógica de negócios e a camada do banco de dados, o que é ruim. Use-o com cuidado :)
Jean-François Côté

1
Se você criar o banco de dados e os objetos necessários, sp, test e depois desmontá-lo depois, é um teste de unidade. Ele testa uma unidade de trabalho.
Tony Hopkinson

2
Os ganhos de desempenho com o mito dos procedimentos armazenados não foram desmascarados?
Jeffo

9

Há um meio termo que você precisa encontrar. Vi projetos assustadores em que os programadores usam o banco de dados como nada além de um armazenamento de chave / valor muito caro. Já vi outros em que os programadores falham em usar chaves e índices estrangeiros. No outro extremo do espectro, vi projetos em que a maioria, se não toda a lógica de negócios, é implementada no código do banco de dados.

Como você observou, o T-SQL (ou seu equivalente em outros RDBMSs populares) não é exatamente o melhor lugar para codificar lógica de negócios complexa.

Tento criar um modelo de dados razoavelmente decente, usar recursos do banco de dados para proteger minhas suposições sobre esse modelo (ou seja, FKs e restrições) e usar o código do banco de dados com moderação. O código do banco de dados é útil quando você precisa de algo (ou seja, uma soma) que o banco de dados seja muito bom em fazer e pode evitar que você mova um zilhão de registros pela rede quando não precisar deles.


2
Usar o banco de dados como um armazenamento de chave / valor "muito caro" é uma técnica perfeitamente válida, como atestarão as legiões de profissionais do NoSQL.
9263 Robert Harvey

1
@RobertHarvey Você está obviamente correto, mas, de alguma forma, meu instinto continua a insistir em que deve haver uma solução mais simples / barata / rápida do que um banco de dados, se tudo que você precisa é um armazenamento de chave / valor. Preciso aprender mais sobre o NoSQL.
Dan Pichelman #

2
Não vejo o uso de procedimentos armazenados como uma cura para um banco de dados mal projetado.
Jeffo

2
@RobertHarvey, li "armazenamento de chave / valor muito caro" literalmente. O emparelhamento de uma licença Oracle ou SQL Server para algo assim, quando há opções como o MongoDB disponíveis gratuitamente, parece desperdiçar dinheiro.
Raphael

@Raphael Ou você poderia usar o PostgreSQL 😉
Demi

9

Se sua lógica de negócios envolve operações de conjunto, provavelmente é um bom lugar para ele no banco de dados, porque os sistemas de banco de dados são realmente bons em executar operações de conjunto.

http://en.wikipedia.org/wiki/Set_operations_(SQL)

Se a lógica de negócios envolver algum tipo de cálculo, provavelmente ela estará fora do procedimento de banco de dados / armazenamento, pois os bancos de dados não são realmente projetados para loop e cálculo.

Embora essas regras não sejam difíceis e rápidas, é um bom ponto de partida.


6

Não existe uma resposta certa para isso. Depende do que você usa o banco de dados. Em um aplicativo corporativo, você precisa da lógica no banco de dados através de chaves estrangeiras, restrições, gatilhos etc. porque é o único local em que todos os aplicativos possíveis compartilham código. Além disso, colocar a lógica necessária no código geralmente significa que o banco de dados é inconsistente e os dados são de baixa qualidade. Isso pode parecer trivial para um desenvolvedor de aplicativos que apenas concorda com o funcionamento da GUI, mas garanto que as pessoas que tentam usar os dados nos relatórios de conformidade acham muito irritante e dispendioso quando recebem multas de bilhões de dólares por terem dados que não siga as regras corretamente.

Em um ambiente não regulatório, quando você não se importa tanto com todo o conjunto de registros e apenas um ou dois aplicativos atingem o banco de dados, talvez você consiga manter tudo no aplicativo.


3

Depois de alguns anos, a questão ainda é importante ...

Uma regra prática simples para mim: se for uma restrição lógica ou uma expressão onipresente (instrução única), coloque-a no banco de dados (sim, chaves estrangeiras e restrições de verificação também são lógicas de negócios!). Se for processual, contendo loops e ramificações condicionais (e realmente não pode ser transformado em uma expressão), coloque-o no código.

Evitar DBs de despejo de lixo

Tentativas de colocar realmente toda a lógica de negócios no código do aplicativo provavelmente degenerarão o banco de dados (relacional) em um lixeira, onde o design relacional é quase totalmente omitido, onde os dados podem ter qualquer estado inconsistente e a normalização está ausente (geralmente XML, JSON , CSV etc. colunas da lixeira).

Esse tipo de lógica somente de aplicativo é provavelmente uma das principais razões para o surgimento do NoSQL - é claro, com a desvantagem de que o aplicativo precisa cuidar de toda a lógica em si, o que foi incorporado ao DB relacional por décadas. No entanto, os bancos de dados NoSQL são mais adequados para esse tipo de manipulação de dados, por exemplo, documentos de dados mantêm uma "integridade relacional" implícita dentro de si. Para bancos de dados relacionais, é simplesmente abuso, causando ainda mais problemas.

Expressões (baseadas em conjunto) em vez de código processual

Na melhor das hipóteses, toda consulta ou operação de dados deve ser codificada como uma expressão, em vez de código processual. Um ótimo suporte para isso é quando as linguagens de programação suportam expressões, como LINQ no mundo .NET (infelizmente, apenas consultas atualmente, sem manipulação). No lado do banco de dados relacional, foi ensinado por um longo tempo a preferir expressões de instrução SQL em vez de loops de cursor procedurais. Portanto, o banco de dados pode otimizar, executar a operação em paralelo ou o que for útil.

Utilize mecanismos de integridade de dados de banco de dados

Quando se trata de RDBMS com restrições de Chave estrangeira e verificação, colunas calculadas, possivelmente gatilhos e visualizações, este é o local para armazenar a lógica comercial básica no banco de dados. A normalização adequada ajuda a manter a integridade dos dados, para garantir uma instância única e distinta dos dados. Mesmo se você precisar duplicá-lo no código e no banco de dados, esses mecanismos básicos de integridade dos dados não devem ser omitidos!

Procedimentos armazenados?

Atualmente, os procedimentos armazenados raramente são necessários, pois os bancos de dados mantêm planos de execução compilados para SQL e os reutilizam quando a mesma consulta volta, apenas com parâmetros diferentes. Portanto, o argumento de pré-compilação para SPs não é mais válido. Pode-se armazenar ou gerar automaticamente consultas SQL no aplicativo ou ORM, que encontrará planos de consulta pré-compilados na maioria das vezes. SQL é uma linguagem de expressão, desde que você não use explicitamente elementos procedimentais. Portanto, na melhor das hipóteses, você usa expressões de código que podem ser traduzidas para SQL.

Enquanto o lado do aplicativo, incluindo o ORM gerado, SQL, não está mais dentro do banco de dados, ao contrário dos Procedimentos Armazenados, continuo contando-o como código do banco de dados. Como ainda requer conhecimento de SQL e banco de dados (exceto o CRUD mais simples) e, se aplicado corretamente, funciona muito diferente do código de procedimento geralmente criado com linguagens de programação como C # ou Java.


2

Realmente depende do negócio, sua cultura e legado. Além de considerações técnicas (estas foram abordadas pelos dois lados), as respostas dadas dizem que se trata de onde as pessoas vêm. Em algumas organizações, os dados são o rei e o DBA é uma figura poderosa. Este é o seu ambiente centralizado típico, um data center com vários terminais conectados a ele. A preferência neste tipo de ambiente é clara. A área de trabalho pode mudar radicalmente várias vezes antes que algo mude no data center e haverá pouco tempo entre elas.

A outra extremidade do espectro é a arquitetura pura de três camadas. Ou talvez multicamada em um negócio orientado para a Web. Você provavelmente ouvirá uma história diferente aqui. O DBA, se houver, será apenas um ajudante que executa algumas tarefas administrativas.

Um desenvolvedor de aplicativos dos tempos modernos terá mais afinidade com o segundo modelo. Se você cresceu com um grande sistema cliente-servidor, provavelmente estaria no outro campo.

Muitas vezes há muitos fatores não técnicos relacionados ao ambiente envolvidos aqui, não há resposta geral para essa pergunta.


2

O termo lógica de negócios está aberto à interpretação. Ao construir sistemas, queremos garantir a integridade do banco de dados e seu conteúdo. Como primeira etapa, deve haver diferentes concessões de acesso de usuários. Como um exemplo muito simples, vamos considerar um aplicativo ATM.

Para obter o saldo da conta, fazer uma seleção em uma exibição apropriada deve funcionar. Mas, para transferir fundos, você deseja que a transação seja encapsulada por um procedimento armazenado. A lógica de negócios não deve ter permissão para atualizar diretamente as tabelas dos valores de crédito e débito.

Neste exemplo, a lógica de negócios pode verificar o saldo antes de solicitar a transferência ou simplesmente chamar o processo armazenado para a transferência e relatar a falha. IMHO, a lógica de negócios, neste exemplo, deve verificar preventivamente se há fundos suficientes disponíveis e se a conta de destino existe e somente então invocar os fundos de transferência. Se outro débito ocorrer entre as etapas iniciais e a chamada do procedimento armazenado, somente um erro será retornado.


Bom exemplo e explicação.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.