Создание службы RESTful API в Go без повторяющихся шаблонов

Есть много материалов о том, как писать сервисы, где сначала нужно выбрать какой-то фреймворк для использования, потом идет разводка обработчиков, конфигов, логов, хранилища и т.д., не говоря уже о том, чтобы где-то развернуть этот сервис. Мы пишем сервисы уже довольно давно, и чаще всего вы просто хотите пропустить весь этот утомительный процесс склейки вещей и просто написать какой-нибудь полезный код.

Вот почему мы создали инструмент под названием Mify — это генератор шаблонов инфраструктуры с открытым исходным кодом, который поможет вам создать сервис, используя лучшие практики, используемые на сегодняшний день. Итак, в этом уроке мы покажем, как создать простой сервис с помощью Mify на классическом примере — приложение для работы с задачами.

Предпосылки

Прежде чем приступить к этому руководству, вот ссылка на полный пример: https://github.com/mify-io/todo-app-example.

Создание проекта

Сначала нам нужно инициализировать рабочее пространство проекта, это будет место, где будет создана внутренняя служба, но вы также можете создать больше служб и внешних интерфейсов. Запустите эти команды:

$ mify init todo-app
$ cd todo-app

После входа в новую рабочую область запустите:

$ mify add service todo-backend

Теперь это создаст шаблон Go для вашего бэкэнда. Вот упрощенное дерево рабочей области со всеми сгенерированными файлами:

.
├── go-services
│   ├── cmd
│   │   ├── dev-runner
│   │   │   └── main.go
│   │   └── todo-backend
│   │       ├── Dockerfile
│   │       └── main.go
│   ├── go.mod
│   ├── go.sum
│   └── internal
│       ├── pkg
│       │   └── generated
│       │       ├── configs
│       │       │   └── ...
│       │       ├── consul
│       │       │   └── ...
│       │       ├── logs
│       │       │   └── ...
│       │       └── metrics
│       │           └── ...
│       └── todo-backend
│           ├── app
│           │   ├── request_extra.go
│           │   ├── router
│           │   │   └── router.go
│           │   └── service_extra.go
│           └── generated
│               ├── api
|               |   └── ...
│               ├── app
│               │   └── ...
│               ├── apputil
│               │   └── ...
│               └── core
│                   └── ...
├── schemas
│   └── todo-backend
│       ├── api
│       │   └── api.yaml
│       └── service.mify.yaml
└── workspace.mify.yaml

Mify примерно следует макету из https://github.com/golang-standards/project-layout, который, несмотря на название репозитория, не является стандартным, но довольно распространенным для сервисов Go. В internal/pkg/generated есть общие библиотеки для конфигураций, журналов и метрик, которые можно повторно использовать для нескольких сервисов. Каталог вашего сервиса находится в internal/todo-backend .

На данный момент этот сервис довольно пустой, поэтому нам нужно добавить к нему API.

Определение API

Mify позволяет вам определить API со схемой OpenAPI — таким образом вы получите готовые обработчики без написания большого количества шаблонов. Более того, Mify также генерирует структурированные журналы и метрики, на написание которых вручную ушли бы недели.

Вы можете найти схему OpenAPI для todo-backend в файле schemas/todo-backend/api/api.yaml. Каталог Schemas в корне рабочей области — это место, где хранятся все конфигурации сервисов, относящиеся к Mify.

Давайте создадим простой CRUD API для вашего бэкенда todo:

  • POST /todos для добавления новых заметок о задачах.
  • PUT,GET,DELETE /todos/{id} для их обновления, получения и удаления.

Вот как будет выглядеть ваша схема OpenAPI для этого API:

Замените предыдущую схему этой и запустите mify generate . Вы можете запускать его каждый раз, когда обновляете схему, и он будет восстанавливать все измененные данные.

Создание и тестирование

Теперь, когда мы добавили обработчики, есть что протестировать, так что давайте сделаем это. В терминале внутри рабочей области Mify перейдите в каталог go-services и запустите службу:

$ cd go-services
$ go mod tidy
$ go run ./cmd/todo-backend

Вы должны увидеть такие журналы запуска:

Вы можете увидеть порт службы в сообщении журнала starting api server, скопировать его в Postman и попробовать вызвать какой-нибудь обработчик API:

Вы можете видеть, что обработчик ничего не вернул, чего и следовало ожидать, поскольку, как следует из ошибки, он еще не реализован.

Добавление моделей и фиктивного хранилища

Давайте начнем добавлять логику в этот сервис. В этом уроке мы будем следовать простой структуре, подобной DDD (Domain-driven design).

Во-первых, нам нужно создать модель todo-заметки, мы поместим ее в пакет domain.

in go-services/internal/todo-backend/domain/todo.go :

Это также хорошее место для определения интерфейса для хранилища, что полезно для отделения логики постоянства от приложения. В этом руководстве мы будем использовать фиктивное хранилище в памяти, но Mify также поддерживает Postgres, который мы можем добавить позже в следующей статье. Поместим хранилище в go-services/internal/todo-backend/storage/todo_mem.go :

Прежде чем переходить к обработчикам, мы также должны реализовать прикладной уровень, чтобы отделить логику хранения, но в этом примере этот уровень будет просто прокси для него.

internal/todo-backend/application/todo.go :

На этом все по логике, осталось добавить ее в обработчики.

Реализация обработчиков

Mify генерирует заглушки обработчика в go-services/internal/todo-backend/handlers/<path/to/api>/service.go . В нашем случае это будут

go-services/internal/todo-backend/handlers/todos/service.go для метода POST и

go-services/internal/todo-backend/handlers/todos/id/service.go для других.

Вот пример заглушки метода POST:

Теперь давайте реализуем все обработчики.

go-services/internal/todo-backend/handlers/todos/service.go

Не забудьте обновить импорт:

import (
 "net/http"
 "strconv"

 "example.com/namespace/todo-app/go-services/internal/todo-backend/domain"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/api"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/apputil"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/core"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/handlers"
)

go-services/internal/todo-backend/handlers/todos/id/service.go

А вот и импорт для них:

import (
 "errors"
 "fmt"
 "net/http"
 "strconv"

 "example.com/namespace/todo-app/go-services/internal/todo-backend/domain"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/api"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/apputil"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/core"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/handlers"
 "example.com/namespace/todo-app/go-services/internal/todo-backend/storage"
)

Логика обработчика довольно проста, мы просто конвертируем сгенерированные модели OpenAPI в наше приложение и обратно, и чтобы избежать дублирования в коде, вот хелпер для создания ответа TodoNode, который используется в этих реализациях:

go-services/internal/todo-backend/handlers/common.go :

Тестирование снова

Наконец-то сервис полностью реализован и мы можем его протестировать!

Сначала мы можем добавить новую заметку todo с запросом POST:

Проверьте, добавлен ли он с помощью запроса GET:

Обновите его с помощью запроса PUT:

Удали это:

И снова запустите GET, чтобы проверить, был ли он удален:

Что дальше

В этой статье еще много чего не описано, например:

  • Постоянное хранилище, такое как Postgres,
  • Конфигурация,
  • промежуточное ПО аутентификации,
  • Развертывание в облаке.

Большая часть этого материала описана в наших документах, так что ознакомьтесь с ними: https://mify.io/docs, но следите за обновлениями для следующих статей.