Olá, você! Muitos questionam 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 poucas horas e colocar tudo em uma base 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 grande
- 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á praticamente de repente. O idioma de programação Golang tem tudo para isso.
Contudo, notei que o idioma já é rico em projetos muito fixes que resolviam 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 em Go
- GORM: Biblioteca ORM
- Protocol Buffers (Protobuf): Melhor forma de serializar dados estruturados.
Nós não as revisaremos no contexto 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 a 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 (é gratuito).
E depois adicione a extensão Golang:
Digite seu primeiro código:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
E execute-o:
go run main.go
Pronto! A próxima etapa é escolher a melhor opção para coletar nossos dados.
Nossa Estrutura de Tarefa
Acho que nossa primeira estrutura deve ser muito 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 precisamos 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 pequeno serviço, 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 defina 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 nosso 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
- Modelo – Estruturas para dados
- Casos de Uso – Camadas de negócios 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”.
- Inicializar nossa aplicação ->
go mod init todo-service
. Vamos inicializar tudo; o gerenciador de dependências criará arquivos e adicionará tudo o que precisamos. - Adicionar 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 concentrar em apenas um método – adicionar tarefas. Para explorar further 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 seguintes comandos:
go get "go.mongodb.org/mongo-driver/mongo"
e
go get "go.mongodb.org/mongo-driver/mongo/options"
Então, escreva um pedaço de código:
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!")
}
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()
// Definir opções de 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!")
// 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 pendentes: executar serviço e desligar
}
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. Em seguida, 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. No meu caso, gostaria muito de usar MongoDB como banco de dados principal se tiver documentos. É muito fácil de gerenciar.
Pode também se observar 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 posso discordar de você. O idioma Go ainda é baseado em paradigmas de linguagens de programação como C++ e Java, onde existe OOP. Tais códigos e metodologias permitem manter o código o mais claro e limpo possível.
Ao começar, será bom consolidar tais fatores como uma 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. Eu notei que o Go 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 uma exploração adicional e exemplos de código completos, visite o repositório GitHub (vinculado anteriormente).
Agradeço e cuide-se!
Source:
https://dzone.com/articles/build-a-to-do-list-with-mongodb-and-golang