Construir una Lista de Tareas con MongoDB y Golang

¡Hola, gente! Muchos han querido saber cómo funcionan las hojas de tarea simples o aplicaciones que proporcionan esa funcionalidad. En este artículo, los invito a considerar cómo pueden escribir su pequeño servicio en Go en un par de horas y poner todo en una base de datos.

Comencemos nuestro viaje con Golang y MongoDB.

¿Por qué Golang?

 

Quiero mostrar las claves:

  • Diseño minimalista y compilación rápida
  • Modelo de concurrencia fuerte con Goroutines y canales
  • Ecosistema enorme
  • Plataforma cruzada de la caja

Un factor más es no gastar mucho tiempo estudiando bibliotecas o soluciones de código abierto. En mi caso, quiero crear un microservicio que funcione casi fuera de la caja. El lenguaje de programación Golang tiene todo para esto.

De todos modos, subrayaré que el lenguaje ya es rico en proyectos muy cool que resuelven muchos problemas para el desarrollador. Proyectos como:

  1. Gin: Framework web de HTTP de alto rendimiento
  2. Viper: Solución de configuración (JSON, propiedades, archivos YML) para aplicaciones de Go
  3. GORM: Biblioteca de ORM
  4. Protocol Buffers (Protobuf): La mejor forma de serializar datos estructurados.

No los revisaremos en el marco del artículo, pero quizás escriba algo sobre ellos más adelante. Con Protobuf, ya he escrito un interesante artículo, “FDe JSON a FlatBuffers: Enhancing Performance in Data Serialization

Instalación

Instalación del lenguaje Go

Visita golang.org y descárgalo. Después, ve al terminal y compruébalo.

Shell

 

go version

IDE

Sólo tienes que instalar VS Code (es gratis).

Y después añade la extensión Golang:

Escribe tu primer código:

Go

 

package main

import "fmt"

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

Y ejecútalo:

Shell

 

go run main.go

¡Y ya está! El siguiente paso es elegir la mejor opción para recoger nuestros datos.

Estructura de nuestra tarea

Creo que nuestra primera estructura debe ser realmente sencilla. Empecemos con 3 campos:

  1. Título (texto)
  2. Descripción (texto)
  3. Estado (bool)

Archivo JSON como referencia:

JSON

 

{     
  "title": "Go to the groceries",     
  "description": "Purchase milk, eggs, and bread",     
  "completed": false 
}

¿Por qué MongoDB?

Necesitamos recopilar datos para nuestras tareas y ser flexibles. No necesitamos crear un esquema o relación entre algo.

  1. Esquema Flexible
  2. Escalabilidad: Soporta escalado horizontal.
  3. Lenguaje de Consultarico

Para nuestro pequeño servicio, lo podemos ejecutar como docker-compose.

YAML

 

 # Use root/example como usuario/contraseña de credenciales
version: '3.1'

services:

 mongo:
   image: mongo
   ports:
     - "27017:27017"
   environment:
     MONGO_INITDB_ROOT_USERNAME: root
     MONGO_INITDB_ROOT_PASSWORD: example

Prefiero Compass GUI para trabajar con nuestros datos (colecciones, bases de datos, etc.). Descárgalo aquí.

Solamente ejecútalo y establezca sus credenciales en “Opciones avanzadas“.Funciona perfectamente y puede ayudarle a encontrar problemas y optimizar solicitudes si las necesita.

Diseño de Sistema (Realmente Pequeño)

Por supuesto, para nuestro servicio, la mejor opción es crear métodos CRUD (Create, Read, Update, Delete), algo como esto (sin eliminar):

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)

Me gustaría usar la manera de separar todas las responsabilidades por carpetas.

  • Manejador – Capa HTTP
  • Modelo – Estructuras para datos
  • Casos de Uso – Capas de negocio con servicio y repositorio

La estructura del proyecto podría ser como esta:

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, tenemos un archivo “go.mod”, ¿pero qué es esto? Es un gestor de paquetes o gestor de dependencias. Podemos instalar y agregar bibliotecas externas y utilizarlas igualmente. Para nuestro ejemplo, necesitamos un par de comandos utilizando “go mod”.

  1. Iniciamos nuestra aplicación -> go mod init todo-service . Iniciamos todo; el gestor de dependencias creará archivos y agregará todo lo que necesitamos.
  2. Agregar dependencia extra utilizando go mod add "enlace" .

Puedes leer más en la página de Referencia de Módulos de Go .

A continuación, centremos nuestro enfoque en solo un método: agregar tareas. Para explorar más allá y ver ejemplos de código completos, visita el repositorio de GitHub Golang Workshop .

Conexión a MongoDB

Podemos crear una conexión utilizando solo dos dependencias:

Go

 

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

Por supuesto, necesitamos agregarla a nuestro servicio. Utilice los siguientes comandos:

go get "go.mongodb.org/mongo-driver/mongo"

y

go get "go.mongodb.org/mongo-driver/mongo/options"

Luego, escriba un pedazo de código:

Go

 

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// Configurar las opciones del cliente de 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!")
}

Estructuras de datos para nuestra aplicación:

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"`
}

  • Tarea – para solicitud HTTP, MongoTask – para la capa de MongoDB: Utilizar dos estructuras es fácil porque a veces no necesitamos enviar datos adicionales a nuestros usuarios. Por ejemplo, podríamos tener un campo secreto, como un nombre de usuario, que debemos ocultar.

1. Capa de repositorio:

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. Capa de servicio:

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. Capa de manejador (para procesar solicitudes 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)
}

Ahora, necesitamos instalar la conexión a la base de datos y inicializar la dependencia “layers” en el archivo main.go.

Go

 

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// Configurar opciones del cliente de 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 repositorio, servicio y manejador
	todoRepo := repository.NewMongoRepository(client)
	todoService := service.NewService(todoRepo)
	todoHandler := handler.NewHandler(todoService)

	// Configurar rutas
	http.HandleFunc("/api/v1/add", todoHandler.AddTask)

	// Crear un servidor
	srv := &http.Server{
		Addr:    ":8080",
		Handler: nil,
	}
  // tarea pendiente: ejecutar servicio y apagar
}

Y probarlo con la solicitud:

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

Es todo. Luego, debemos agregar y implementar nuevos métodos, y nuestro servicio estará listo para funcionar.

Conclusión

Hemos creado un pequeño, pero robusto servicio de gestión de tareas utilizando Golang y MongoDB.

Como pueden ver, si necesitamos construir un pequeño servicio, lo hacemos realmente rápido sin muchos obstáculos. En mi caso, realmente me gustaría usar MongoDB como base de datos principal si tengo documentos. Es fácil de administrar.

También se puede notar que no sería peor en otras lenguajes de programación. En algunos lugares, incluso es más rápido. Por ejemplo, si utilizas Python y FastAPI – y aquí puedo discutir con ti. El lenguaje Go aún se basa en los paradigmas de lenguajes de programación como C++ y Java, donde hay OOP. Este código y metodología permiten mantener el código lo más claro y limpio posible.

Para empezar, será bueno consolidar factores como una base, que te ayudarán a entender TDD y otras metodologías. Además, observaré que para evitar sobrecargar el artículo con métricas y texto, omití comparar el rendimiento con otras lenguas. Notaré que Go no tiene problemas con esto, y habiendo escrito la función principal, ya la ejecutas en tu hilo. Puedes encontrar fácilmente comparaciones en Internet, incluyendo benchmark.

Para explorar más a fondo y ejemplos de código completos, visita el repositorio de GitHub (enlace propuesto anteriormente).

Gracias y cuídate!

Source:
https://dzone.com/articles/build-a-to-do-list-with-mongodb-and-golang