Bom dia! Muitas pessoas questionam-se sobre como funcionam as simples planilhas de tarefas ou aplicações que oferecem essa funcionalidade. Neste artigo, eu convido você a considerar como você pode escrever seu pequeno serviço em Go em poucos horas e colocar tudo em um banco de dados.
Vamos começar nossa viagem com o Golang e o MongoDB.
Por que Golang?
Eu quero mostrar as chaves:
- Design minimalista e compilação rápida
- Modelo de concorrência forte com Goroutines e canais
- Ecosistema enorme
- Plataforma cruzada na caixa
Outro fator é não gastar muito tempo estudando bibliotecas ou soluções open-source. Em meu caso, eu quero criar um microserviço que funcionará quase de uma das embalagens. O idioma de programação Golang tem tudo para isso.
Contudo, notei que o idioma já érico em projetos muito fixes que resolvam muitos problemas para o desenvolvedor. Projetos como:
- Gin: Framework HTTP de alto desempenho
- Viper: Solução de configuração (JSON, propriedades, arquivos YML) para aplicações Go
- GORM: Biblioteca ORM
- Protocol Buffers (Protobuf): Melhor maneira de serializar dados estruturados.
Nós não as revisaremos dentro do framework do artigo, mas talvez eu escreva algo sobre elas depois. Com Protobuf, eu já escrevi um artigo interessante, “From JSON to FlatBuffers: Enhancing Performance in Data Serialization.”
Instalação
Instalando Linguagem Go
Visite golang.org e faça o download. Em seguida, vá para o terminal e verifique.
go version
IDE
Apenas instale VS Code (é grátis).
E depois adicione a extensão Go:
Digite seu primeiro código:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
E execute-o:
go run main.go
É assim! O próximo passo é escolher a melhor opção para coletar nossos dados.
Nossa Estrutura de Tarefa
Acho que nossa primeira estrutura deve ser mesmo simples. Vamos começar com 3 campos:
- Título (texto)
- Descrição (texto)
- Status (bool)
Arquivo JSON como referência:
{
"title": "Go to the groceries",
"description": "Purchase milk, eggs, and bread",
"completed": false
}
Por que MongoDB?
Nós precisamos coletar dados para nossas tarefas e ser flexíveis. Não precisamos criar um esquema ou relação entre as coisas.
- Estrutura Flexível
- Escalabilidade: Ele suporta escalonamento horizontal.
- Linguagem de Consulta Rica
Para noss serviço pequeno, podemos executá-lo com docker-compose
.
# Use root/example como credenciais de usuário/senha
version: '3.1'
services:
mongo:
image: mongo
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
Eu prefiro o Compass GUI para trabalhar com nossos dados (coleções, bancos de dados, etc.). Baixe aqui.
Execute-o e configure suas credenciais em “Opções Avançadas“. Funciona perfeitamente e pode ajudá-lo a encontrar problemas e otimizar solicitações se necessário.
Design de Sistema (Muito Pequeno)
Para noss serviço, claro, a melhor opção é criar métodos CRUD (Criar, Ler, Atualizar, Excluir), algo como isto (sem excluir):
http.HandleFunc("/api/v1/add", todoHandler.AddTask)
http.HandleFunc("/api/v1/get-all", todoHandler.GetAllTasks)
http.HandleFunc("/api/v1/update", todoHandler.UpdateTask)
http.HandleFunc("/api/v1/complete", todoHandler.CompleteTask)
Eu quero usar a maneira de separar todas as responsabilidades por pastas.
- Handler – Camada HTTP
- Model – Estruturas para dados
- Casos de Uso – Camadas de negócio com serviço e repositório
A estrutura do projeto pode ser assim:
todo-list/
│
├── cmd/
│ └── main.go
├── pkg/
│ └── handler
│ └── add_task.go
│ └── http_handler.go
│ └── complite_task.go
│ └── get_all_task.go
│ └── update_task.go
│ └── mapper
│ └── task.go
│ └── model
│ └── task.go
│ └── usecase
│ └── task
│ └── repository
│ └── add_task.go
│ └── complite_task.go
│ └── get_all_task.go
│ └── mongo_repositiry.go
│ └── repository.go
│ └── update_task.go
│ └── service
│ └── add_task.go
│ └── complite_task.go
│ └── get_all_task.go
│ └── service.go
│ └── update_task.go
└── go.mod
Como podemos ver, temos um arquivo “go.mod”, mas o que é ele? É um gerenciador de pacotes ou gerenciador de dependências. Podemos instalar e adicionar bibliotecas externas e usá-las também. Para o nosso exemplo, precisamos de algumas instruções usando “go mod”.
- Inicie sua aplicação ->
go mod init todo-service
. Vamos iniciar tudo; o gerenciador de dependências criará arquivos e adicionará tudo o que precisamos. - Adicione dependência extra usando
go mod add "link"
.
Você pode ler mais na Página de Referência de Módulos Go .
Então, vamos focar em apenas um método – adicionar tarefas. Para explorar mais e ver exemplos de código completos, visite o repositório GitHub Golang Workshop .
Conexão com o MongoDB
Usando apenas duas dependências, podemos criar uma conexão:
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
Claro, precisamos adicioná-lo ao nosso serviço. Use os comandos:
go get "go.mongodb.org/mongo-driver/mongo"
e
go get "go.mongodb.org/mongo-driver/mongo/options"
Depois, escreva um pedaço de código:
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Defina as opções do cliente do MongoDB
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017").SetAuth(options.Credential{
Username: "root",
Password: "example",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal(err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
log.Println("Connected to MongoDB!")
}
Estruturas de dados para nossa aplicação:
package model
import "go.mongodb.org/mongo-driver/bson/primitive"
type Task struct {
ID string `json:"id"`
Title string `json:"title"`
Desciption string `json:"description"`
Completed bool `json:"completed"`
}
type MongoTask struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Title string `json:"title"`
Desciption string `json:"description"`
Completed bool `json:"completed"`
}
Tarefa
– para solicitação HTTP,MongoTask
– para camada MongoDB: Usar duas estruturas é fácil porque, às vezes, não precisamos enviar dados adicionais para nossos usuários. Por exemplo, podemos ter um campo secreto, como um nome de usuário, que deve ser oculto.
1. Camada de repositório:
type Repository interface {
AddTask(ctx context.Context, task model.MongoTask) error
}
func (r *MongoRepository) AddTask(ctx context.Context, task model.MongoTask) error {
task.ID = primitive.NewObjectID()
_, err := r.collection.InsertOne(ctx, task)
}
2. Camada de serviço:
type TodoService interface {
AddTask(ctx context.Context, task model.Task) error
}
func (s *Service) AddTask(ctx context.Context, task model.Task) error {
return s.Repo.AddTask(ctx, mapper.MapToDto(task))
}
3. Camada de manipulador (para processar solicitações HTTP):
func (h *Handler) AddTask(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
var task model.Task
err := json.NewDecoder(r.Body).Decode(&task)
if err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
err = h.Service.AddTask(ctx, task)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
}
Agora, precisamos instalar a conexão com o banco de dados e inicializar a dependência “layers” no arquivo main.go
.
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Configurar opções do cliente MongoDB
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017").SetAuth(options.Credential{
Username: "root",
Password: "example",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal(err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
log.Println("Connected to MongoDB!")
// Inicializar repositório, serviço e manipulador
todoRepo := repository.NewMongoRepository(client)
todoService := service.NewService(todoRepo)
todoHandler := handler.NewHandler(todoService)
// Configurar rotas
http.HandleFunc("/api/v1/add", todoHandler.AddTask)
// Criar um servidor
srv := &http.Server{
Addr: ":8080",
Handler: nil,
}
// tarefa pendente: executar serviço e encerrar
}
E testá-lo com a solicitação:
# curl -X POST http://localhost:8080/add
#-H "Content-Type: application/json"
#-d '{
# "id": 1,
# "title": "Comprar alimentos",
# "completed": false
#}'
POST http://localhost:8080/api/v1/add
Content-Type: application/json
{
"title": "Add description to the structure",
"description": "your desc here..."
}
Essa é tudo. Então, devemos adicionar e implementar novos métodos, e o nosso serviço estará pronto para funcionar.
Conclusão
Criamos um pequeno, mas robusto serviço de gerenciamento de tarefas usando Golang e MongoDB.
Como podemos ver, se precisarmos construir um pequeno serviço, fazemos isso muito rápido sem muitos obstáculos. Em meu caso, eu realmente gostaria de usar MongoDB como banco de dados principal se eu tivesse documentos. É muito fácil de gerenciar.
Pode-se também notar que não seria pior em outras linguagens de programação. Em algumas partes, até é ainda mais rápido. Por exemplo, se você usar Python e FastAPI – e aqui eu possivelmente discordo de você. O idioma Golang ainda se baseia em paradigmas de linguagens de programação como C++ e Java, onde existe OOP. Tais códigos e metodologias permitem manter o código tão claro e limpo quanto possível.
Para começar, vai ser bom consolidar tais fatores como base, o que ajudará você a entender TDD e outras metodologias. Eu também notei que, para evitar sobrecarregar o artigo com métricas e texto, eu omiti a comparação com outras linguagens. Notei que o Golang não tem problemas com isso, e tendo escrito a função principal, você já a executa em sua thread. Você pode facilmente encontrar comparações na Internet, incluindo benchmarks.
Para explorar mais a fim e ver exemplos de código completos, visite o repositório GitHub (vinculado anteriormente).
Agradecimentos e cuide-se!
Source:
https://dzone.com/articles/build-a-to-do-list-with-mongodb-and-golang