Atenção: post grande, algumas opiniões, conclusão vaga 'faça o que funciona melhor para você'
Geralmente, isso é feito como um meio de implementar a 'arquitetura hexagonal' em torno do seu banco de dados. Você pode ter aplicativos da Web, aplicativos móveis, aplicativos de desktop, importadores em massa e processamento em segundo plano, consumindo seu banco de dados de maneira uniforme. Certamente você poderia realizar a mesma coisa, até certo ponto, escrevendo uma biblioteca rica para acessar seu banco de dados e fazendo com que todos os seus processos usassem essa biblioteca. E, de fato, se você estiver em uma pequena loja com um sistema muito simples, esse é provavelmente o melhor caminho a percorrer; É uma abordagem mais simples e, se você não precisa dos recursos avançados de um sistema mais complicado, por que pagar pela complexidade? No entanto, se você estiver trabalhando com um conjunto grande e sofisticado de sistemas que precisam interagir com o banco de dados em grande escala,
Independência e manutenção da plataforma
Se você tem um banco de dados e escreve uma biblioteca Python para interagir com esse banco de dados, e todo mundo puxa essa biblioteca para interagir com o banco de dados, isso é ótimo. Mas digamos que de repente você precise escrever um aplicativo móvel, e esse aplicativo móvel agora também precise conversar com o banco de dados. E seus engenheiros iOS não usam Python, e seus engenheiros Android não usam Python. Talvez o pessoal do iOS queira usar as linguagens da Apple e os engenheiros do Android queiram usar Java. Então você ficaria preso escrevendo e mantendo sua biblioteca de acesso a dados em 3 idiomas diferentes. Talvez os desenvolvedores de iOS e Android decidam usar algo como o Xamarin para maximizar o código que podem compartilhar. Perfeito, exceto que você provavelmente ainda precisará portar sua biblioteca de acesso a dados para o .NET. E então sua empresa acabou de comprar outra empresa que ' O aplicativo da web é um produto díspar, mas relacionado, e a empresa deseja integrar alguns dos dados da plataforma da sua empresa na plataforma da subsidiária recém-adquirida. Só há um problema: a subsidiária era uma start-up e decidiu escrever a maior parte de sua aplicação no Dart. Além disso, por qualquer motivo (provavelmente fora do seu controle), a equipe móvel que estava pilotando o Xamarin decidiu que não era para eles e que preferia usar as ferramentas e os idiomas específicos para os dispositivos móveis para os quais desenvolverá. Mas enquanto você estava nessa fase, sua equipe já havia entregue uma grande parte da sua biblioteca de acesso a dados no .NET, e outra equipe da empresa estava escrevendo algumas coisas malucas de integração do Salesforce e decidiu fazer tudo isso no .NET desde lá. já era uma biblioteca de acesso a dados.
Portanto, agora, devido a uma reviravolta muito realista, você tem sua biblioteca de acesso a dados escrita em Python, .NET, Swift, Java e Dart. Eles não são tão legais quanto você gostaria que fossem. Você não pode usar um ORM da maneira mais eficaz que deseja, porque cada idioma possui ferramentas ORM diferentes; portanto, você precisa escrever mais código do que gostaria. E você não foi capaz de dedicar tanto tempo a cada encarnação quanto gostaria, porque há cinco deles. E a versão Dart da biblioteca é especialmente complicada, porque você precisava criar coisas transacionais para algumas delas, porque as bibliotecas e o suporte simplesmente não estavam lá. Você tentou argumentar que, por causa disso, o aplicativo Dart deveria ter apenas a funcionalidade somente leitura do seu banco de dados, mas a empresa já havia decidido que quaisquer recursos que planejassem valiam o esforço extra. E acontece que há um erro em algumas lógicas de validação que existem em todas essas encarnações da sua biblioteca de acesso a dados. Agora você deve escrever testes e códigos para corrigir esse bug em todas essas bibliotecas, obter revisões de código para suas alterações em todas essas bibliotecas, obter controle de qualidade em todas essas bibliotecas e lançar suas alterações em todos os sistemas usando todos os essas bibliotecas. Enquanto isso, seus clientes estão descontentes e adotaram o Twitter, reunindo combinações de vulgaridades que você nunca imaginou que poderiam ser concebidas, muito menos direcionadas ao principal produto de sua empresa. E o proprietário do produto decide não ter muita compreensão da situação.
Por favor, entenda que em alguns ambientes, o exemplo acima é tudo menos artificial. Também leve em consideração que essa sequência de eventos pode se desdobrar ao longo de alguns anos. Geralmente, quando você chega ao ponto em que arquitetos e pessoas de negócios começam a falar sobre como conectar outros sistemas ao banco de dados, é nesse momento que você deseja 'colocar uma API REST na frente do banco de dados' no seu roteiro. Considere, desde o início, quando ficou claro que esse banco de dados começaria a ser compartilhado por alguns sistemas, que um serviço da web / API REST foi colocado na frente dele. Corrigir o erro de validação seria muito mais rápido e fácil, porque você está fazendo uma vez em vez de cinco vezes. E liberar a correção seria muito mais fácil de coordenar, porque você '
TLDR; É mais fácil centralizar a lógica de acesso a dados e manter clientes HTTP muito finos do que distribuir a lógica de acesso a dados para cada aplicativo que precisa acessar os dados. De fato, seu cliente HTTP pode até ser gerado a partir de metadados. Em sistemas grandes, a API REST permite manter menos código
Desempenho e escalabilidade
Algumas pessoas podem acreditar que conversar com o banco de dados diretamente, em vez de passar primeiro por um serviço da web, é mais rápido. Se você tem apenas um aplicativo, isso certamente é verdade. Mas em sistemas maiores, eu discordo do sentimento. Eventualmente, em algum nível de escala, será muito benéfico colocar algum tipo de cache na frente do banco de dados. Talvez você esteja usando o Hibernate e queira instalar uma grade Infinispan como um cache L2. Se você possui um cluster de 4 servidores robustos para hospedar seu serviço da Web separado de seus aplicativos, pode se dar ao luxo de ter uma topologia incorporada com replicação síncrona ativada. Se você tentar colocar isso em um cluster de 30 servidores de aplicativos, a sobrecarga de ativar a replicação nessa configuração será muito grande; É necessário executar o Infinispan em um modo distribuído ou em algum tipo de topologia dedicada, e de repente o Hibernate precisa sair pela rede para ler a partir do cache. Além disso, o Infinispan funciona apenas em Java. Se você tiver outros idiomas, precisará de outras soluções de cache. A sobrecarga de rede de ter que passar do aplicativo para o serviço da web antes de chegar ao banco de dados é rapidamente compensada pela necessidade de usar soluções de cache muito mais complicadas que geralmente vêm com sobrecarga própria.
Além disso, essa camada HTTP da API REST fornece outro mecanismo valioso de armazenamento em cache. Seus servidores para sua API REST podem colocar cabeçalhos de cache em suas respostas, e essas respostas podem ser armazenadas em cache na camada de rede, que se adapta excepcionalmente bem. Em uma configuração pequena, com um ou dois servidores, sua melhor aposta é usar apenas um cache de memória no aplicativo quando ele se comunica com o banco de dados, mas em uma grande plataforma com muitos aplicativos em execução em vários servidores, você deseja aproveitar o rede para lidar com o armazenamento em cache, porque, quando configurado corretamente, algo como squid, verniz ou nginx pode ser expandido para níveis insanos em hardware relativamente pequeno. Centenas de milhares ou milhões de solicitações por segundo de taxa de transferência são muito mais baratas a partir de um cache HTTP do que de um servidor de aplicativos ou de um banco de dados.
Além disso, ter vários clientes apontados para o banco de dados, em vez de apontar para alguns servidores que, por sua vez, apontam para o banco de dados, pode dificultar muito o ajuste do banco de dados e do pool de conexões. Em geral, a maior parte da carga de trabalho real em um servidor de aplicativos é material de aplicativo; esperar que os dados retornem do banco de dados geralmente consome tempo, mas geralmente não é muito caro em termos computacionais. Você pode precisar de 40 servidores para lidar com a carga de trabalho do seu aplicativo, mas provavelmente não precisa de 40 servidores para orquestrar a busca dos dados no banco de dados. Se você dedicar essa tarefa a um serviço da Web, o serviço da Web provavelmente estará em execução em muito menos servidores que o restante do aplicativo, o que significa que você precisará de muito menos conexões com o banco de dados. O que é importante, porque os bancos de dados geralmente não
TLDR; É mais fácil ajustar, dimensionar e armazenar em cache o acesso aos dados quando ocorre algo dentro de um único serviço da web dedicado do que quando ocorre em vários aplicativos diferentes usando diferentes idiomas e tecnologias
Pensamentos finais
Por favor, não se afaste desse pensamento "Ah, uau, eu sempre deveria estar usando APIs REST para obter meus dados" ou "Esse idiota está tentando dizer que estamos fazendo errado, porque nosso aplicativo Web conversa diretamente com o banco de dados, mas nossas coisas funcionam bem! " . O ponto principal que estou tentando destacar é que sistemas diferentes e empresas diferentes têm requisitos diferentes; Em muitos casos, colocar uma API REST na frente do seu banco de dados realmente não faz sentido. É uma arquitetura mais complicada que requer justificação dessa complexidade. Mas quando a complexidade é justificada, há muitos benefícios em ter a API REST. Ser capaz de avaliar as diferentes preocupações e escolher a abordagem certa para o seu sistema é o que faz um bom engenheiro.
Além disso, se a API REST estiver atrapalhando a depuração, provavelmente haverá algo errado ou faltando nessa imagem. Não acredito que ter essa camada de abstração adicionada intrinsecamente torne a depuração mais difícil. Quando trabalho com sistemas grandes de n camadas, gosto de ter um contexto de log distribuído. Talvez quando um usuário inicia uma solicitação, gere um GUID para essa solicitação e registre o nome de usuário desse usuário e a solicitação que ele fez. Em seguida, passe esse GUID enquanto seu aplicativo se comunica com outros sistemas. Com agregação e indexação de log adequadas, você pode consultar toda a plataforma para o usuário que está relatando o problema e ter visibilidade de todas as suas ações, e elas passam pelo sistema para identificar rapidamente onde as coisas deram errado. Novamente, é uma arquitetura mais complicada,
Fontes:
http://alistair.cockburn.us/Hexagonal+architecture
https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing