Abstraindo bibliotecas web em aplicações Go
Em 2015, depois de mais de uma década usando PHP como minha linguagem de programação principal, eu comecei a desenvolver projetos em Go. Um dos primeiros impactos que tive nessa migração foi a ausência de frameworks web como o Zend Framework, Symfony ou Laravel, todos muito bem estabelecidos na comunidade PHP.
Depois de alguns estudos entendi que a abordagem adotada pela comunidade Go é bem diferente da que eu estava acostumado. Ao invés de adotarem frameworks complexos as pessoas desenvolvedoras prezam ao máximo por seguir os provérbios da linguagem, como “Clear is better than clever”. Para atingir isso, entre outras coisas, as pessoas fazem uso extensivo da ótima biblioteca padrão da linguagem.
Go, possui uma biblioteca formada por vários pacotes muito úteis, e um deles é o http que entrega funcionalidades e define interfaces padrão para a implementação de clientes e servidores HTTP. Com base nestas interfaces surgiram várias bibliotecas que facilitam o desenvolvimento. Como por exemplo:
…entre outras.
Ter esta gama de opções dá liberdade aos times, mas pode ser problemático em equipes grandes, para casos em que cada projeto use uma biblioteca diferente. Para resolver esse problema aqui no PicPay, desenvolvemos uma biblioteca que abraça as interfaces da biblioteca padrão, assim garantimos compatibilidade sem limitar os times na escolha de uma em especial.
Neste artigo, mostrarei alguns trechos de código para demonstrar o conceito. Foi necessário ocultar alguns detalhes internos, mas acredito que o código possa ser facilmente adotado em outros projetos, pois é relativamente simples.
Criando a abstração
Dado o seguinte projeto de exemplo:
Dentro do pacote internal/api/, criei o arquivo api.go:
A função Start recebe a porta que o servidor HTTP vai ouvir, alguma implementação da interface http.Handler e uma lista de parâmetros. Essa lista de parâmetros é usada para configurar timeouts de leitura e escrita. Caso a pessoa não passe essas configurações, é usado o timeout padrão de 30 segundos. Com estas informações recebidas a função pode fazer a inicialização de um servidor HTTP, já com a configuração de Graceful shutdown, que é uma boa prática.
Implementando os handlers
O próximo passo é implementarmos a interface http.Handler usando uma das bibliotecas. Por exemplo, vamos implementar primeiramente usando a lib Gin. Para isso, criei o pacote internal/http/gin e dentro dele o arquivo book.go:
Na função main da nossa aplicação podemos agora fazer o uso das duas implementações para iniciarmos o servidor HTTP, e respondermos as requisições do usuário. Para isso, no arquivo cmd/api/main.go:
Caso algum time deseje usar a lib Fiber, basta desenvolver uma implementação da interface http.Handler, como no exemplo do arquivo internal/http/fiber/book.go:
E a única alteração necessária no main.go será mudar a inicialização dos handlers de h := gin.Handlers(s) para h := fiber.Handlers(s) e todo o resto vai funcionar.
Adicionando funcionalidades globais
Outra vantagem, é que agora podemos usar a função Start do pacote api para adicionar funcionalidades “globais” da nossa aplicação, com o uso de middlewares. No arquivo internal/api/api.go eu fiz duas alterações. A primeira foi a criação de uma nova função:
E a segunda alteração foi na função Start, para fazermos uso deste middleware. Para facilitar o gerenciamento de middlewares usei uma biblioteca chamada Negroni, que deixa o código mais organizado. O trecho que eu alterei ficou da seguinte forma:
Isso é bem útil caso seja preciso adicionar funcionalidades como coleta de métricas, controle de acessos, rate limits, etc.
A dica final que gostaria de reforçar é sempre que estiver desenvolvendo aplicações em Go, tente se manter o mais fiel possível às interfaces fornecidas pela biblioteca padrão da linguagem. Dessa forma, você garantirá mais compatibilidade com as versões, além de menor dependência de bibliotecas e frameworks externos.