Construindo uma Lista de Tarefas com MongoDB e Golang

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:

  1. Gin: Framework HTTP de alto desempenho
  2. Viper: Solução de configuração (JSON, propriedades, arquivos YML) para aplicações em Go
  3. GORM: Biblioteca ORM
  4. 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.

Shell

 

go version

IDE

Apenas instale VS Code (é gratuito).

E depois adicione a extensão Golang:

Digite seu primeiro código:

Go

 

package main

import "fmt"

func main() {
   fmt.Println("Hello, World!")
}

E execute-o:

Shell

 

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:

  1. Título (texto)
  2. Descrição (texto) 
  3. Status (bool)

 Arquivo JSON como referência:

JSON

 

{     
  "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.

  1. Estrutura Flexível
  2. Escalabilidade: Ele suporta escalonamento horizontal.
  3. Linguagem de Consulta Rica

Para noss pequeno serviço, podemos executá-lo com docker-compose.

YAML

 

 # 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):

Go

 

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:

Plain Text

 

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”.

  1. 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.
  2. 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

 

	"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:

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!")
}

Estruturas de dados para nossa aplicação:

Go

 

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:

Go

 

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:

Go

 

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):

Go

 

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.

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:

HTTP

 

# 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