Привет, други! Многие спросили, как работают простые задачи листов или приложения, которые обеспечивают такую functionality. В этой статье я приглашаю вас рассмотреть, как вы можете написать свой небольшой сервис на Go за несколько часов и положить все в базу данных.
Пусть начнем нашу поездку с Go и MongoDB.
为什么 Go?
Я хочу показать ключи:
- Минималистический дизайн и быстрое сборка
- Сильная конкурентная модель с помощью Goroutines и каналов
- Огромная экосистема
- 跨平台 сразу
Другим фактором является то, что нужно немного времени для изучения библиотек или opensource решения. В моем случае я хочу создать микросервис, который будет работать практически из коробки. Язык программирования Go имеет все для этого.
然而, я хочу отметить, что язык уже богат в очень крутых проектах, которые решают множество проблем для разработчика. Такие проекты, как:
- Gin: High-performance HTTP web framework
- Viper: Configuration solution (JSON, properties, YML files) for Go applications
- GORM: ORM library
- Protocol Buffers (Protobuf): The best way to serialize structured data.
Мы их не будем рассматривать в рамках статьи, но, возможно, я позже напишу что-то о них. С Protobuf у меня уже было написано интересное сообщение “From JSON to FlatBuffers: Enhancing Performance in Data Serialization.”
Установка
Установка языка Go
Посетите golang.org и скачайте. Затем перейдите в терминал и проверьте его.
go version
IDE
Просто установите VS Code (он бесплатный).
А затем добавьте расширение Go:
Введите свой первый код:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
И запустите его:
go run main.go
Это все! Следующий шаг – выбрать лучшую опцию для сбора наших данных.
Структура нашей задачи
Я думаю, что наша первая структура должна быть очень проста. Начнем с трех полей:
- Заголовок (текст)
- Описание (текст)
- Статус (логическое значение)
JSON-файл как справка:
{
"title": "Go to the groceries",
"description": "Purchase milk, eggs, and bread",
"completed": false
}
为什么MongoDB?
Мы должны собирать данные для наших задач и быть гибкими. Нам не нужно создавать схему или связи между какими-либо вещами.
- Гибкая Схема
- Скалярность: она поддерживает горизонтальное масштабирование.
- ric Query Language
Для нашего небольшого сервиса мы можем запустить его как docker-compose
.
# Использовать 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 для работы с нашими данными (коллекциями, базами данных и т. д.). Скачайте его здесь.
Просто запустите его и установите свои учетные данные в “Дополнительные опции“. Он работает отлично и может помочь вам найти проблемы и оптимизировать запросы, если вам это нужно.
СистемноеDesign (очень маленькое)
Конечно, для нашего сервиса лучшим вариантом будет создание методов CRUD (Create, Read, Update, Delete), чем-то вроде этого (без delete):
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)
Я хочу использовать способ разделения всех обязанностей по папкам.
- Handler – HTTP层次
- Model – Структуры для данных
- Use cases – Бизнес-слои с сервисами и репозиториями
Структура проекта может быть следующей:
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
Как我们可以 see, у нас есть файл “go.mod”, но что это за файл? Это manager packer или manager зависимостей. Мы можем устанавливать и добавлять внешние библиотеки и использовать их, как нам нужно. Для нашего примера, нам нужны пара команд с использованием “go mod”.
- Init нашего app ->
go mod init todo-service
. Мы инициализируем все; manager зависимостей создаст файлы и добавит все, что нам нужно. - Добавить дополнительную зависимость с использованием
go mod add "link"
.
Вы можете больше узнать на странице Go Modules Reference .
Тогда давайте сосредоточимся только на одном методе — добавления задач. Чтобы получить больше информации и полные примеры кода, посетите репозиторий GitHub Golang Workshop .
Соединение с MongoDB
Utilizing только двух зависимостей, мы можем создать соединение:
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
Конечно, нам нужно добавить его в нашу службу. Utilize команды:
go get "go.mongodb.org/mongo-driver/mongo"
и
go get "go.mongodb.org/mongo-driver/mongo/options"
После этого, напишите кусок кода:
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Set MongoDB client options
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!")
}
Data структуры для нашего app:
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. Слой репозитория:
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. Слой сервиса:
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-запросов:
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
.
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: запуск сервиса и выключение
}
И тестировать его с запросом:
# 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..."
}
}’ That’s all. Затем, мы должны добавить и реализовать новые методы, и наш сервис будет готов работать.
Заключение
Мы создали небольшой, yet robust сервис управления задачами с использованием Golang и MongoDB.
Как我们可以 see, если мы нужны для создания небольшого сервиса, мы делаем это очень быстро без множества препятствий. В моем случае, я действительно хотел бы использовать MongoDB в качестве основной базы данных, если у меня есть документы. Это просто управляется.
Также можно отметить, что было бы не хуже и в других программных языках. В некоторых местах, это даже быстрее. Например, если вы используете Python и FastAPI – и здесь я могу быть в разном мнении с вами. Язык Go до сих пор основан на парадигмах программирования, таких как C ++ и Java, где есть OOP. Такие code и методология позволяют maintain code как можно более ясным и чистым.
начинать с понимания таких факторов, как базовая часть, которая поможет вам понять TDD и другие методологии. также отмечу, что для устранения перегрузки статьи данными и текстом я пропустил сравнение скорости с другими языками. заметить, что у Go нет проблем с этим, и having written главную функцию, вы уже запустите ее в вашем потоке. Вы легко можете найти сравнения в интернете, включая벤чмаркс.
Для дальнейшего изучения и полных примеров кода посетите репозиторий GitHub (указанный ранее).
Спасибо и осторожно!
Source:
https://dzone.com/articles/build-a-to-do-list-with-mongodb-and-golang