GoLang, desbravando uma linguagem de programação - Parte 3 (Final)

GoLang, desbravando uma linguagem de programação - Parte 3 (Final)

Este artigo é a terceira e ultima parte de uma sequência de artigos sobre meu processo de aprendizado de uma nova linguagem de programação, nessa série tenho o objetivo de documentar esse processo na esperança de talvez poder ajudar alguém, não só aprender a própria linguagem Go, como qualquer outra linguagem de programação. A parte 1 dessa série pode ser lida aqui e a parte 2 aqui.

Antes de começarmos

Após algum tempo aprendendo Golang, confesso que configurar uma ambiente funcional com go e docker não foi das tarefas mais simples. Após muita leitura e varias tentativas e erros acabei por adotar um modelo hibrido, onde uso o docker apenas para testar no ambiente mais próximo ao ambiente de produção, mas para o desenvolvimento acabei por configurar o ambiente local 😒. Assim simplifiquei bem dockercompose.yml e removi o Dockerfile que havia criado no artigo anterior.

Frameworks no mundo Go

O uso de frameworks em Go é viável, mas nem sempre necessário. Ao adentrar no ecossistema de Go é possível perceber o quão rico ele é, hoje mesmo sendo uma linguagem relativamente nova, é possível perceber um número enorme de opções de bibliotecas que abrangem a grande maioria das necessidades que um desenvolvedor pode ter em seu dia a dia, podendo assim tecer sua própria solução sem maiores dificuldades.

Dias atrás tive acesso a um repositório que tem o intuito de catalogar e classificar os principais pacotes disponíveis para uso, pode ver ele aqui. Ele é realmente completo, tem dezenas de categorias e diversas opções para cada uma delas, certamente vale uma conferida.

Ao acessar https://github.com/avelino/awesome-go#web-frameworks perceberá uma lista considerável com diversos frameworks disponíveis para o desenvolvimento de aplicações web, que será o objetivo do projeto final dessa dessa série. Desses, por indicação da própria comunidade no slack¹ ( que por sinal recomendo muito que todos que tenham interesse na linguagem participe), e baseado também em minhas pesquisas, echo e gin são os dois mais expressivos hoje.

O uso de frameworks é controverso para iniciantes em uma linguagem. É fácil perceber como eles podem dar aquele gás para quem ainda está iniciando, mostrando bons caminhos, boas escolhas de design para a aplicação e muito mais. Porém é comum ver eles sendo usados como muleta para desenvolvedores que realmente não aprenderam sobre a linguagem e seu ecossistema. Assim cabe a quem esta iniciando a escolha, ou usa ele como escada ou como muleta, eu prefiro usar como escada, e esse processo me ajuda muito a aprender e me ambientar.

Entre os frameworks destacados muitas similaridades são perceptíveis, tanto o echo como o gin tem um foco muito claro em performance, chegando a prometer performance ainda maior que teríamos se usarmos o módulo nativo. E isso faz muito sentido, se um dos maiores diferenciais da linguagem é a alta performance, é natural que os frameworks que compõem seu ecossistema também sigam a mesma linha.

Nesse ponto é fundamental que leiam a documentação dos frameworks que parecem mais promissores, com a documentação poderá alinhar se os objetivos e funcionalidades do mesmo alinham com as do projeto.

Implementando uma API em Golang

Bom agora é hora da parte legal, bora colocar a mão na massa e criar uma API restfull para entender melhor os desafios e as possibilidades que o ecossistema de Go disponibiliza. O objetivo da api é simular um microservice de autenticação extremamente simplista e sem nenhuma pretensão de levá-lo para produção.

As principais features esperadas para essa implementação são:

  • Cadastro de um novo usuário
  • Atualização de usuário já cadastrado
  • Remoção de usuário existente
  • Listagem com todos os usuários cadastrados
  • Validar um usuário por seu nome de usuário e senha

Para essa implementação resolvi usar o echo como suporte para me prover as funcionalidades básicas para desenvolver essa API.

Iniciando um novo projeto

Para iniciar um novo projeto go, já dentro da nova pasta crie um arquivo de nome main.go e inclua o seguinte conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package main

import (
"net/http"

"golang.org/x/crypto/bcrypt"
"github.com/labstack/echo"
)

type User struct {
Email string `json:"email,omitempty" form:"email"`
Password string `json:"-" form:"password"`
Name string `json:"name,omitempty" form:"name"`
Role string `json:"role,omitempty" form:"role"`
}

var users []User

// Retorna a listagem de todos os usuários cadastrados
func getAllUsers (c echo.Context) error {
return c.JSON(http.StatusOK, users)
}

// Retorna os dados de um usuário
func getUser (c echo.Context) error {
email := c.Param("user")
user := getUserByEmail(email)
return c.JSON(http.StatusOK, user)
}

// Cadastra um novo usuário
func createUser (c echo.Context) error {
password := c.FormValue("password")
hashPassword, _ := bcrypt.GenerateFromPassword([]byte(password), 10)
user := User{Email: c.FormValue("email"), Password: string(hashPassword), Name: c.FormValue("name"), Role: c.FormValue("role")}
users = append(users, user)
return c.JSON(http.StatusCreated, user)
}

// Atualiza um usuário existente
func updateUser (c echo.Context) error {
email := c.Param("user")
for index, user := range users {
if (user.Email == email) {
name := c.FormValue("name");
if ( name != "") {
users[index].Name = name
}

role := c.FormValue("role");
if ( role != "") {
users[index].Role = role
}
password := c.FormValue("password");
if ( password != "") {
hashPassword, _ := bcrypt.GenerateFromPassword([]byte(password), 10)
users[index].Password = string(hashPassword)
}
return c.JSON(http.StatusOK, users[index])
}
}

return c.JSONBlob(http.StatusBadRequest, []byte("{\"status\": \"error\"}"))
}

// Exclui um usuário existente
func deleteUser (c echo.Context) error {
email := c.Param("user")
for index, user := range users {
if (user.Email == email) {
users = append(users[:index], users[index+1:]...)
return c.JSONBlob(http.StatusOK, []byte("{\"status\": \"success\"}"))
}
}

return c.JSONBlob(http.StatusBadRequest, []byte("{\"status\": \"error\"}"))
}

// Faz a autenticação de um usuário
func authUser (c echo.Context) error {
email := c.FormValue("email")
password := c.FormValue("password")

user := getUserByEmail(email)
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
if (err == nil) {
return c.JSONBlob(http.StatusOK, []byte("{\"status\": \"success\"}"))
}

return c.JSONBlob(http.StatusUnauthorized, []byte("{\"status\": \"error\"}"))
}

// Retonar instancia do usuário apartir de um e-mail
func getUserByEmail(email string) User {
var user User;
for _, user = range users {
if (user.Email == email) {
return user
}
}

return user;
}

func main() {
// Inicia usuário
password, _ := bcrypt.GenerateFromPassword([]byte("123456"), 10)
users = append(users, User{Email:"[email protected]", Password: string(password), Name:"Ronaldo Meneguite", Role:"Admin"})

router := echo.New()
v1 := router.Group("/v1")

// User API
v1.GET("/users", getAllUsers)
v1.GET("/users/:user", getUser)
v1.POST("/users", createUser)
v1.PUT("/users/:user", updateUser)
v1.DELETE("/users/:user", deleteUser)

// Auth API
v1.POST("/auth", authUser)

router.Logger.Fatal(router.Start(":8080"))
}

E execute o seguinte comando:

1
dep init

Após o processo é esperado que seja criado na pasta corrente uma nova de nome vendor que contém as dependências do projeto, e dois novos arquivos Gopkg.toml e Gopkg.lock, onde o Gopkg.toml será responsável por manter o registros das dependências diretas do projeto e o Gopkg.lock de armazenar as versões de todos os packages instalados atualmente.

Feito o processo, já temos nossa primeira API pronta para ser executada.

Para isso execute o comando:

1
go run main.go

Assim feito já teremos nossa API rodando no endereço http://localhost:8080.

Como puderam perceber, trabalhar com Golang não tem muitos mistérios, é certamente uma linguagem muito promissora e possui tempos de resposta realmente impressionantes, como pode ser visto abaixo:

Levando nossa API a Produção

Bom com tudo pronto e rodando localmente é hora de ver como seria colocar uma aplicação desenvolvida em Go em produção. Para isso decidi usar a Google Cloud Platform usando o serviço de App Engine.

Esse artigo não tem o intuito de mostrar como fazer todo o processo de deploy usando o Google Cloud Platform, assim me limitarei a demostrar os processos feitos dentro do projeto para coloca-lo em produção e descrever minha experiência com o processo.

Depois de criar o projeto no console é necessário criar um arquivo de nome app.yaml que deve possuir o seguinte conteúdo:

1
2
runtime: go
env: flex

E executar o seguinte comando:

1
gcloud app deploy

Após alguns minutos já terá uma sua API completamente funcional e em produção, e o que parece magica por tanta simplicidade, na verdade é um casamento perfeito entre o sistema de deploy do Google e sua linguagem.

Conclusão

Hoje ainda não me vejo conseguindo uma produtividade próxima da que tenho em PHP ou Javascript em Golang, principalmente em projetos monolíticos maiores, mas acho que esse jogo pode mudar muito a medida que os microservices entram em cena. Go parece nascer perfeito para esse fim, é confiável, rápido, simples e altamente escalável, certamente foi uma excelente aquisição para minha “caixa de ferramentas” e espero ter oportunidade de trabalhar diretamente com ela em breve.

Comentários

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×