Executando verificação de segurança...
15

Tornando a Experiência do Desenvolvimento Mais Eficiente com Backend for Frontend (BFF): Uma Jornada de Adoção e Implementação

Neste artigo quero compartilhar uma boa experiência que tive com o BFF, trazer os pontos que me fizeram escolher essa forma de arquitetura para o desenvolvimento de um projeto na empresa onde trabalho!

Do começo

Trabalhei muito com um bom e funcional formato de desenvolvimento: O Backend ‘CRUD’ e o Frontend cuidando do tratamento de dados e requests.
Por mais que seja funcional e todos conhecem, comecei a trabalhar em telas que demoravam muito para carregar totalmente, uma request aqui, outra ali, uma condicional aqui, …, muito trefego de dados nessa historia, com essa dor, procurei mentoria com colegas mais experientes buscando uma solução, aí lançaram para mim o BFF!

O BFF

Bom, para definir o BFF por uma explicação rápida temos o seguinte entendimento: é uma API que vai encapsular uma resposta personalizada para diferentes UIs, como uma aplicação em mobile e uma web.

Sem nos aprofundarmos podemos tirar alguns pontos positivos e negativos como:

  • Ponto super positivo: Todo dado da aplicação vai ter tratado internamente, uma mega camada de segurança
  • Ponto positivo: O backend não precisa expor muitas rotas
  • Ponto positivo: As regras de negócio estão todas no backend
  • Ponto positivo: A UI vai ter a responsabilidade somente da própria UI e UX
  • Ponto neutro: é uma melhoria do V do pattern MVC

TOP! É essa arquitetura que quero usar em novos projetos!

Implementação

Agora vem a parte boa, código, e para isso quero montar uma telinha para exemplificar uma implementação do BFF

Telinha

Tela de carrinho, onde o usuário pode acionar/remover produtos no carrinho, pesquisar produtos e fechar a compra.

No primeiro passo podemos começar a montar a tela, em html mesmo:

<!DOCTYPE html>
<html>


<head>
  <title>Shopping Cart</title>
</head>


<body>
  <div>
      <div id="sidebar">
        <button></button>
        <a>Carrinho</a>
        <p>Total: R$ 7,00</p>
        <button>Finalizar compra</button>
      </div>
      <div id="main">
        <input type="text" placeholder="Pesquisar">
        <div id="produto-1">
          <img >
          <h2>Meu produto aqui</h2>
          <p>Descrição e talz</p>
          <p>R$ 7,00</p>
          <div><button>-</button><input type="text" value="1"><button>+</button></div>
        </div>
        <div id="produto-2">
          <img >
          <h2>Meu produto aqui</h2>
          <p>Descrição e talz</p>
          <p>R$ 14,00</p>
          <div><button disabled>-</button><input type="text" value="0"><button disabled>+</button></div>
        </div>
      </div>
  </div>
</body>


</html>

Tela montada, ta praticamente pronta, porem os dados precisam vir do backend, algo assim:

{
  sidebar: {
    backButton: {
      text: "←"
      action: "goBack"
    },
    title: "Carrinho",
    details: {
      value: "R$ 7,00"
      label: "Total:"
    },
    checkoutButton: {
      text: "Finalizar compra"
      action: "request",
      href: "/checkout/cart-123"
      method: "POST",
      onSuccess: {
        action: "goTo",
        href: "/checkout/cart-123"
      },
    }
  },
  main: {
    search: {
      placeholder: "Pesquisar"
    },
    products: [
      {
        id: "produto-1",
        image: "https://via.placeholder.com/150",
        title: "Meu produto aqui",
        description: "Descrição e talz",
        price: "R$ 7,00",
        quantity: {
          value: 1,
          min: 0,
          max: 10
        },
        buttons: {
          quantityUpdate: {
            action: "request",
            href: "/checkout/cart-123/product-1",
            method: "POST",
            onSuccess: {
              action: "refresh"
            },
            body: {
              quantity: "::quantity::"
            }
          },
          product: {
            action: "goTo",
            href: "/product/1"
          }
        }
      },
      {
        id: "produto-2",
        image: "https://via.placeholder.com/150",
        title: "Meu produto aqui",
        description: "Descrição e talz",
        price: "R$ 14,00",
        quantity: { value: 0 },
        Disabled: true,
        buttons: {
          product: {
            action: "goTo",
            href: "/product/2"
          }
        }
      }
    ]
  }
}

Dinamica

Agora com uma conversa mais aprofundada, e trazendo mais sobre o fluxo, a dinâmica de comunicação do frontend com o backend vai ser a seguinte:

Frontend

O frontend vai 'iniciar', podendo trazer um loading, e, ao mesmo tempo, chamar a nossa rota BFF no backend(/api/screens/cart), o retorno vai ser usado para montar a tela.

Sobre a montagem da tela temos já todos os valores formatados e traduzidos pelo backend, e também em ações temos o direcionamento(A famosa regra de negócio) já definidas, incluindo campos 'habilitados' ou não.

Na parte do direcionamento, vamos ter as 'actions' apontando oque fazer:

  • goBack: faz a tela voltar
  • goTo: le a propriedade de destino e move o usuário de tela
  • request: direciona o frontend a fazer uma ação no backend
  • onSuccess: é uma possível recursividade para um novo direcionamento
  • refresh: recarrega a tela(OBS: Praticamente todos frameworks de frontend tem o controle dos componentes que precisam ser alterados, com isso se o json de retorno tiver somente o 'price' diferente, é esse o único componente que vai ser atualizado)

Backend

O backend é o cara que vai ter toda a responsabilidade nas costas, regras de negócio, disponibilidade de dados e desempenho!

Tendo somente uma rota para retornar a tela inteira montada, e outras 2 para ações do usuário(finalizar compra e alterar quantidade)
Tendo acesso a todos os dados, pode buscar por uma personalização da tela para o usuário, procurando por preferências como linguagem e moeda
Limitar os dados retornados para o frontend, evitando disponibilizar dados sensíveis
Ter muito mais velocidade em buscar dados, pois já está na nuvem

Extra

O backend pode ajudar muito o frontend, indo além, imagina agora eu adicionar em cada produto uma prop de searchTerms, nela vou colocar todos os termos possíveis que aquele produto pode responder, algo como: ['productCode', 'title', 'colecation', 'tag'], quando o frontend for fazer o filtro da pesquisa, está muito mais fácil saber oque filtrar.
E na mesma linha, imagina que tenhamos na sidebar um botão de filtro rápido "Filtar produtos que estou comprando", para ajudar o frontend, o banckend pode trazer no produto uma prop de buying: true para o filtro.

Também comentando que tanto no frontend, quanto no backend, é possível criar componentes que são especialistas que alguma parte do JSON do BFF. Um botão que é genérico o suficiente para tratar as actions sozinho.

E se eu não usasse o BFF

Trazendo somente comparação, creio que teríamos estruturalmente esta aparência no projeto:

  • Uma rota para carregar o usuário
  • Uma rota para calcular o carrinho
  • Uma rota para listar os produtos
  • Uma rota para informar o estoque
  • O frontend precisando de um 'localStore' para facilitar o controle da tela
  • O frontend tendo que fazer várias requests, consumindo de certa forma muita 'banda'
  • O frontend tento que utilizar processamento para modelar todos os dados
  • O frontend tento que ter as regras de negocio 'clonadas'

Trabalho futuro e Considerações finais

Estou estudando para que esse backend BFF possa responder em partes(Trabalhar com streams), assim podendo responder partes da tela antes que outras, ou até atualizando alguns valores após ter a tela carregada...

Sou um dev fullstack e essa abordagem que apresentei neste post vem me agradando muito, estou tendo bastante desempenho em construir soluções novas bem solidas e de qualidade.

Ficarei muito feliz em receber sugestões, dúvidas ou até mesmo criticas!

Obrigado por ler o meu post :D

2

Parabéns pelo artigo, muito bem escrito, sem lero lero e com exemplos, curti a leitura do começo ao fim.

Este modelo parece ser superior a um SSR, pois resolve algumas das desvantagens do SSR ao mesmo tempo que permite uma separação de responsabilidades sem adicionar tanta complexidade em troca.

2

Esse método de desenvolvimento tem nos auxiliado muito mesmo na empresa em que trabalhos. (Eu e o autor trabalhamos na mesma "firma").

Temos conversado sobre alguns pontos de implementação para o futuro. Dentre estas, destaco:

  • A criação de componentes no backend.
    • Esses componentes retornariam um objeto padrão que seria consumido pelo front. Podendo ser alterado para suprir a necessidade de cada especificidade. Ex: a action, que retorna um padrão para o button. Ja retornaria dados padrão, que podem ser alterados para serem usados na tela específica... como label, url, icon, color, size, etc, etc.
  • Uma prop de form
    • Isso poderia conter diversos inputs, que seriam distribuidos em tela, linha a linha. Contendo ainda uma prop de size, (seguindo os padrões do bootstrap, por exemplo), seria possivel dispor em tela diversos inputs para formar um formulário em tela
    • Você pode com isso inclusive validar quais campos são obrigatórios ou não, como será enviado o body na request e todos o necessário. Temos ja um projeto disso, (DynamicForm), mas falta dar um "grau" no componente.

Enfim, a mudança de paradigma em nossa empresa foi muito grande. Em um primeiro momento causa uma certa estranheza e um desconforto em fazer. Mas é só a primeira olhada. Ao iniciar o desenvolvimento já é possivel perceber os benefícios.

Parabéns pelo artigo Sr. Autor! :P

1
1
1

Aqui estou usando pra validar uma ideia uma stack de Go + Templ + HTMX. E tá sendo uma experiência bastante interessante. O HTMX acaba que facilita muita requisição pro backend além de usar Ajax por debaixo dos panos e trazer muita dinâmica pro front.

1
1

Sim, feito tudo no backend, e aí entra 3 possíveis casos:

Se esta trabalhando com microsserviços

  • 1º É plausível criar um microsserviço para fazer esse processamento das respostas para o frontend de toda aplicação. Assim consumindo todos os serviços necessários para montar a resposta para o frontend.

ou

  • 2º Dentro de cada microsserviços criamos os endpoints BFF. Dentro do serviço, podemos utilizar de seus recursos 'localmente' e também fazer o consumo de outros serviços para montar a resposta para o frontend

e se estiver com projeto no formato de monólito

  • 3º A mesma abordagem do caso 2. Complementado também o BFF é bom adicionar um padrão de desenvolvimento tipo um MVC

Para os projetos que trabalho(em microsserviços) vejo mais vantajoso a segunda abordagem