使用 MongoDB 和 Golang 构建待办事项列表

你好!许多人好奇简单的任务清单或提供此类功能的应用程序是如何工作的。在这篇文章中,我将邀请你考虑如何用Go编写你的小型服务,并在几小时内将其放入数据库中。

让我们从Golang和MongoDB开始我们的旅程。

为什么选择Golang?

 

我想展示关键点:

  • 简洁的设计和快速的编译
  • 强大的并发模型,带Goroutines和通道
  • 庞大的生态系统
  • 跨平台直接使用

另一个因素是不想花费太多时间学习库或开源解决方案。在我的案例中,我想创建一个微服务,可以几乎不需要任何设置就能工作。Go编程语言已经具备了这一点所需的一切。

然而,我要指出的是,该语言已经拥有许多非常酷的项目,解决了许多开发者的问题。诸如:

  1. Gin:高性能HTTP网络框架
  2. Viper:为Go应用程序提供配置解决方案(支持JSON,属性,YML文件)
  3. GORM:ORM库
  4. Protocol Buffers (Protobuf):结构化数据序列化的最佳方式

我们不会在本文的框架内回顾它们,但也许我以后会写一些关于它们的内容。关于 Protobuf,我已经写过一篇有趣的文章:”F从 JSON 到 FlatBuffers:

安装

安装 Go 语言

访问 golang.org 并下载。

Shell

 

go version

IDE

安装 VS Code(免费)。

然后添加 Golang 扩展:

键入第一段代码:

Go

 

package main

import "fmt"

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

并运行:

Shell

 

go run main.go

就这样!

我们的任务结构

我认为我们的第一个结构应该非常简单。让我们从 3 个字段开始:

  1. 标题(文本)
  2. 描述(文本)
  3. 状态(bool)

JSON 文件作为参考:

JSON

 

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

为什么选择 MongoDB?

我们需要为我们的任务收集数据,并且要灵活。

  1. 灵活的模式
  2. 可扩展性:
  3. 丰富的查询语言

对于我们的小型服务,我们可以将其作为 docker-compose 运行。

YAML

 

# 使用 root/example 作为用户/密码凭证
version: '3.1'

services:

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

我更喜欢使用 Compass GUI 来处理我们的数据(集合、数据库等)。Download it here.

Just run it and set your credentials to “Advanced options“.

System Design (Really Small)

当然,对于我们的服务来说,最好的选择是创建 CRUD(创建、读取、更新、删除)方法,类似于这样的方法(不含删除):

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)

我想使用按文件夹分隔所有责任的方法。

  • 处理程序 – HTTP 层
  • 模型 – 数据结构
  • 用例 – 带有服务和存储库的业务层

项目结构可能是这样的:

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

正如我们所看到的,我们有一个 “go.mod” 文件,但它是什么呢?它是一个打包管理器或依赖管理器。我们可以安装和添加外部库,并使用它们。对于我们这个例子,我们需要使用 “go mod” 命令来执行几个操作。

  1. 初始化我们的应用 -> 执行 go mod init todo-service命令。我们将初始化所有内容;依赖管理器将创建文件并添加我们所需的一切。
  2. 使用 go mod add "link"命令添加额外的依赖。

您可以在Go Modules 参考页面了解更多相关信息。

然后,让我们专注于一个方法——添加任务。为了进一步探索和完整的代码示例,请访问GitHub仓库Golang 研讨会

连接到MongoDB

仅使用两个依赖项,我们就可以创建一个连接:

Go

 

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

当然,我们需要将其添加到我们的服务中。请使用以下命令:

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

以及

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

然后,编写一段代码:

Go

 

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

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

我们的应用的数据结构:

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

  • 任务 – 对于HTTP请求,MongoTask – 对于MongoDB层:使用两种结构很简单,因为有时候我们不需要向我们的用户发送附加数据。例如,我们可能有一个秘密字段,比如用户名,我们必须隐藏它。

1. 仓库层:

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. 服务层:

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. 处理器层(用于处理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)
}

现在,我们需要在main.go文件中安装数据库连接并初始化“layers”依赖关系。

Go

 

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

	// 设置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!")

	// 初始化仓库、服务和处理器
	todoRepo := repository.NewMongoRepository(client)
	todoService := service.NewService(todoRepo)
	todoHandler := handler.NewHandler(todoService)

	// 设置路由
	http.HandleFunc("/api/v1/add", todoHandler.AddTask)

	// 创建一个服务器
	srv := &http.Server{
		Addr:    ":8080",
		Handler: nil,
	}
  // todo: 运行服务和关闭服务
}

并用以下请求进行测试:

HTTP

 

# curl -X POST http://localhost:8080/add
#-H "Content-Type: application/json"
#-d '{
#    "id": 1,
#    "title": "买杂货",
#    "completed": false
#}'
POST http://localhost:8080/api/v1/add
Content-Type: application/json

{
 "title": "Add description to the structure",
 "description": "your desc here..."
}

这就是全部。然后,我们必须添加并实现新方法,我们的服务将准备好投入使用。

结论

我们使用Golang和MongoDB创建了一个小型但健壮的任务管理服务。

正如我们所看到的,如果我们需要构建一个小型的服务,我们可以非常快速地完成它,而不会遇到很多障碍。在我的案例中,如果我有文档,我真的很想使用MongoDB作为主数据库。它是很容易管理的。

也可以注意到,在其他编程语言中也不会更差。在某些地方,它甚至更快。例如,如果您使用Python和FastAPI,在这点上我可能要与您意见不一致。Go语言仍然基于C++和Java等编程语言的范式,其中存在面向对象编程。这样的代码和方法可以使您保持代码尽可能清晰和干净。

首先,巩固诸如基础等因素将帮助您理解测试驱动开发和其他方法。我还注意到,为了避免文章中充斥着指标和文本,我省略了与其他语言的性能比较。我要指出的是,Go语言在这方面没有问题,编写完主函数后,您已经在您的线程中运行它了。您可以在互联网上轻松找到比较,包括基准测试。

为了进一步探索和完整的代码示例,请访问之前提到的GitHub仓库。

谢谢,保重!

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