Backend For Frontend: Uma estratégia sob medida para a entrega de microsserviços

Allan Oliveira
sanar
Published in
9 min readNov 11, 2020

Você está projetando uma arquitetura de microsserviços do zero ou está evoluindo uma existente? Bem, já estive nessa posição, meu amigo. Você provavelmente há de encarar muitos trade-offs que o farão ficar acordado até tarde, então é melhor estar preparado.

Nos últimos anos, tive a chance de projetar e escalar tal arquitetura e as certezas que tenho são as seguintes: erros e aprendizados fazem parte do caminho. Mas não fique preocupado, tudo dará certo contanto que se adapte rápido, estude e siga alguns bons padrões.

A fim de compartilhar alguns dos aprendizados que adquiri e ajudar outras pessoas, decidi começar uma série de artigos dedicados à arquitetura de microsserviços e este é o primeiro. Espero que seja útil e aproveite a leitura!

Os primeiros passos em microsserviços

O primeiro passo ao entrar no mundo de microsserviços é realmente desenhá-los e a maneira mais eficaz de fazer isto é dividindo sua lógica de negócios em domínios. Pode parecer muito subjetivo no início, mas é um processo contínuo de aprendizado, portanto, é necessário revisar a arquitetura e identificar novos padrões conforme a lógica de negócios evolui.

Não há uma real necessidade e é muito difícil definir tudo logo no início, porque é muito provável que as coisas mudem com o tempo. Além disso, existe o risco de que realizar definições de arquitetura em baixa granularidade com antecedência, para negócios em crescimento acelerado, pode levar a uma refatoração cara e precoce. Como dica, para obter bons designs, há uma eficiente e bem difundida metodologia chamada Domain Driven Design (te conto mais sobre isso em breve).

Uma vez que tenha seus microsserviços bem projetados, desacoplados e funcionando, você deseja que o mundo faça bom uso deles, certo? Com certeza! Bem, agora vem a grande questão: como disponibilizá-los adequadamente?

Neste primeiro artigo, apresentarei a vocês minha perspectiva sobre o assunto, focando em como entregar microsserviços de maneira eficiente para aplicações cliente por meio de alguns bons padrões usados no mercado.

Pensamentos iniciais

Cada microsserviço numa arquitetura de sistemas distribuídos tem seu próprio endereço na rede interna, e um primeiro pensamento pode ser expô-los assim como estão. Desta forma, as aplicações cliente devem estar cientes de todos os microsserviços e irão chamá-los de acordo com o necessário.

Isso pode parecer razoável no início, mas vamos mergulhar um pouco mais fundo. Pense no seguinte cenário: existem 2 aplicações cliente (1 aplicação web e 1 aplicação mobile) e apenas 3 microsserviços. Este é um cenário bem simples, o que torna fácil conectar e dar manutenção na comunicação entre clientes e servidor. No entanto, algumas semanas depois, a lógica de negócio cresce, levando à criação de mais 3 microsserviços.

As coisas estão ficando mais trabalhosas agora. É necessário refletir tais mudanças em cada aplicação cliente. Isso leva à refatoração das chamadas de API em 2 aplicações. Se você acha que esse trabalho tende a ser repetitivo, você acertou.

Espera-se que sua empresa continue crescendo levando ao desenvolvimento de mais microsserviços e aplicações frontend. Manter essas aplicações sincronizadas ficará ainda mais custoso com o passar do tempo. Ninguém quer isso. O que você precisa é de algo para abstrair esse crescimento e reduzir ao máximo a necessidade de refatorações, a fim de concentrar a energia dos engenheiros de software em outras evoluções críticas do negócio.

Este é o primeiro exemplo em que o padrão Composição de API (API Composition, em inglês), também conhecido como API Gateway, e suas variações como Backend For Frontend (BFF) podem ser aplicados.

O padrão Composição de API

Como você deve saber, microsserviços devem ser o mais fracamente acoplados possível, inclusive, tendo cada um seu próprio banco de dados. Mas há uma grande chance de precisarmos compor e correlacionar dados de vários microsserviços para atender às solicitações de aplicações cliente. Quando isso ocorre, existem duas opções que podemos adotar: compor os dados no cliente ou compor os dados no servidor.

A escolha de compor dados nos clientes pode levar a grandes desvantagens em termos de performance ao longo do tempo, devido a várias requisições de ida e volta entre cliente e servidor.

Por exemplo, imagine o seguinte: um cliente precisa fazer 3 requisições para fontes de dados diferentes (neste caso, microsserviços) para exibir os dados necessários por uma única página. Cada solicitação leva centenas de milissegundos para realizar uma viagem de ida e volta cliente-servidor. Por ser uma comunicação WAN (Wide Area Network), a latência varia e o desempenho pode ser degradado principalmente em redes de telefonia móvel.

Por outro lado, no servidor, a latência é muito menor para compor os dados, o que é derivado do fato de que a comunicação entre microsserviços é altamente eficiente desde que os mesmos estejam na mesma rede, ou seja, numa LAN (Local Area Network ), o que é geralmente o caso.

O resultado obtido é que a maior parte do tempo gasto para carregar dados completos se deve as várias viagens de ida e volta entre cliente e servidor. Podemos então concluir que cada viagem de ida e volta cliente-servidor economizada é de extrema importância, e uma abordagem mais apropriada para reduzir a latência total é migrar a lógica de composição, do cliente para o servidor, e substituir as várias requisições por somente o mínimo necessário.

Migrar a composição de dados de microsserviços para o back-end é o que define o padrão Composição de API (isto é, API Gateway). Como podemos ver, este padrão não oferta apenas uma grande abstração, para ocultar a granularidade da arquitetura de microsserviços dos clientes, mas também pode conter lógica de aplicação em si.

Sendo assim, uma aplicação cliente não precisa mais saber onde alguns dados estão localizados e também não vai mais lidar com a composição de dados. Isso reduz drasticamente a complexidade do front-end e a transfere para o back-end, o que permite um front-end mais performático e fácil de dar manutenção no que tange a integração de microsserviços.

Embora a performance seja uma grande vantagem do padrão Composição de API, existe uma lista mais ampla de possíveis benefícios, como assuntos de teor transversal entre os microsserviços, por exemplo: segurança, autenticação, autorização, rate limit e tradução de protocolos de comunicação. A maior sacada aqui é que lidar com tais problemas em uma estrutura semelhante a um API Gateway reduz o esforço de implementação geral, visto que os pontos de mudanças são reduzidos.

Motivação para a variação Backend For Frontend

Conforme o negócio evolui, é provável que surjam mais aplicações de front-end. Algumas podem ser muito semelhantes, mas algumas podem precisar consumir os microsserviços de diferente formas. Normalmente, é aqui que você se pergunta se deve existir outro API Gateway ou não.

Essa é a questão que nos leva a uma variação do padrão Composição de API chamada Backend For Frontend (BFF). O nome é bastante intuitivo e foi apresentado pela primeira vez por Phil Calçado enquanto trabalhava na SoundCloud. A ideia é ter uma única interface de back-end, isto é, um API Gateway, para cada aplicação front-end.

A principal motivação por trás desse padrão está em adaptar, compor e fornecer dados de uma forma que atenda estritamente aos casos de uso de uma aplicação cliente, nem mais nem menos. Conforme o foco do desenvolvimento fica mais próximo da experiência front-end, a eficiência aumenta para o cliente devido ao menor número de solicitações para coleta de dados e menor necessidade de manipulação de dados no front-end.

O padrão BFF também oferece uma experiência mais autônoma às equipes de front-end, principalmente quando existem várias equipes dependendo dos mesmos microsserviços, pois haverá então menos burocracia para realizar alterações para servir dados específicos.

No entanto, surgem mais pontos a serem considerados, visto que não é tão trivial julgar se algumas aplicações front-end têm necessidades específicas que justificam a segregação.

Digamos que cada cliente que você tenha usa um modelo específico de autenticação ou autorização, ou que cada cliente se concentra em um conjunto diferente de casos de uso. Por exemplo, uma aplicação web focada no usuário final e uma aplicação web focada nos usuários administradores. Neste cenário, como há baixa sobreposição entre eles, fica fácil distinguir as necessidades e justificar a existência de diferentes BFFs.

No entanto, se você encontrar uma sobreposição significativa sobre as necessidades dos clientes, fica difícil decidir sobre adotar vários BFFs ou somente um.

Se você decidir por um único BFF, pode começar bem, mas ao longo do tempo as necessidades dos clientes podem variar, levando a um BFF inflado, com acúmulo de adaptações para cada cliente, e difícil de manter. Se você decidir por vários BFFs, há também uma grande chance de duplicação de código e, consequentemente, retrabalho se você não prestar atenção suficiente à visão mais ampla da lógica dessas aplicações.

De toda forma, tenha em mente algumas boas práticas, como desacoplamento e modularidade. Quando há alguma lógica de back-end comum que vale a pena compartilhar entre vários clientes, duas opções são úteis: bibliotecas e serviços.

Extração e reutilização de lógica são práticas bastante comuns, especialmente em uma arquitetura de microsserviços, e aqui está uma dica sólida para definir qual abordagem escolher:

  • Se a lógica muda na medida que uma funcionalidade muda, é uma boa ideia mover a lógica para o microsserviço correlacionado onde essa funcionalidade reside, porque provavelmente está no mesmo domínio de negócio; Lembre-se, como dito anteriormente, o design de microsserviços é um processo contínuo;
  • Se a lógica não muda conforme as funcionalidades mudam ao longo do tempo, ela pode ser extraída e formatada melhor como uma biblioteca, que será então compartilhada;

Outra dica é focar nas features e casos de uso específicos, ao invés de tentar generalizar a implementação para um conjunto mais amplo de casos de uso que cada cliente possui. Do contrário, você pode enfrentar maior complexidade na capacidade de manutenção para atender de maneira abstrata as especificidadesde cada cliente e, em alguns casos, até mesmo atrito entre equipes de desenvolvimento que dependam do mesmo projeto.

Em geral, a combinação microsserviços + BFFs funciona bem, mas existem mais preocupações a serem notadas:

  • Evite dar muita responsabilidade ao BFF. Os donos de cada domínio de negócio devem ser cada um dos diferentes microsserviços. Portanto, um BFF inflado com muita lógica de domínio de negócio é um ótimo candidato para ter tal lógica movida para os microsserviços.
  • Aumento do esforço dos times de engenharia, pois cada BFF é uma nova aplicação que precisa ser desenvolvida, implantada e mantida. Portanto, leve isso em consideração baseado no tamanho do time de engenharia que você faz parte, pois times pequenos podem enfrentar problemas para manter muitas aplicações ao mesmo tempo.

Uma experiência BFF otimizada

Trazer um BFF à vida pode ser trabalhoso. É necessário integrar vários microsserviços num único ponto de entrada, ocasionalmente suportando múltiplos protocolos de comunicação e abstrair toda essa complexidade de seu cliente.

Além disso, por sua definição, os BFFs devem fornecer dados da maneira mais conveniente possível para as necessidades de seus clientes. Isso implica em compor e refinar os dados para atender aos casos de uso e aos requisitos de performance.

Não existe uma abordagem padrão para a implementação de um aplicação BFF. Pode-se implementá-lo por meio de uma API REST tradicional e as expectativas podem ser atendidas. No entanto, nos últimos anos, tem crescido a adesão por uma alternativa chamada GraphQL.

GraphQL é uma linguagem e ambiente de consulta para APIs. GraphQL fornece uma descrição completa e compreensível dos dados em sua API, dá aos clientes o poder de pedir exatamente o que precisam e nada mais, torna mais fácil evoluir APIs ao longo do tempo e habilita ferramentas poderosas de desenvolvedor.

Como afirma o site oficial (https://graphql.org/), as funcionalidades do GraphQL fornecem uma correspondência altamente apropriada para o padrão Backend For Frontend. Ele permite coordenar a comunicação entre vários microsserviços mesclando-os em um esquema GraphQL, habilitando assim a entrega de dados de várias fontes distintas a partir de uma única requisição para a API. Existem ainda outros benefícios bem apreciados do uso do GraphQL, tais como: validação e tipificação por padrão; evolução sem versionamento de API.

Não há solução mágica para a construção de um BFF, e o GraphQL pode levar a problemas se não for implementado adequadamente, porém seus recursos fornecem a solução mais apropriada para esse padrão. Se você ainda não testou o GraphQL, eu recomendo fortemente que o faça.

Conclusão

Projetar uma arquitetura de microsserviços eficiente é um trabalho desafiador. É muito improvável que alguém consiga acertar tudo no início, portanto, a revisão contínua da arquitetura é necessária para identificar gargalos e aplicar os padrões apropriados.

Este artigo serve como uma introdução aos dois padrões: Composição de API e sua variação Backend For Frontend. Mesmo não sendo uma solução mágica, comprovou-se que tais padrões tratam de forma eficiente vários problemas comuns na evolução de arquiteturas de microsserviços.

Espero ter lhe despertado algumas boas idéias. E você, já teve alguma experiência com o assunto? Caso sim, por favor, não deixe de comentar! Mais artigos relacionados a microsserviços estão por vir e feedbacks são sempre bem-vindos!

Referências

--

--

Allan Oliveira
sanar
Writer for

A passionate business problem solver and innovation-driven guy, always improving my skills in Software Engineering & Leadership. linkedin.com/in/allanoliveira1