Русский
Русский
English
Статистика
Реклама

Перевод Пишем CRUD-приложение на Go с помощью Mysql, GORM, Echo, Clean Architecture

Начнем сначала


В этой статье будет сказ о том, как на Clean Architecture написать API с функциями CR(U)D, где в качестве БД взят Mysql, фреймворк Echo, ORMapper GORM.

Что делаем


API с функциями Create, Read, (Update), Delete. Обновление на самом деле реализовать особо не удалось, милости прошу попробовать самостоятельно.

Целевая аудитория


Те разработчики, которые хотят создать простой API после освоения Go.

Основное cодержание


Что такое Clean Architecture, и с чем его едят


Это мы можем подробно рассмотреть на следующей картинке:

image

Цель Clean Architecture это разделение сфер. Чтобы удачно добиться этого разделения, нужно всегда держать в голове зависимости каждого слоя на картинке с остальными. Разделение на слои улучшает читаемость кода и делает его устойчивым к изменениям.

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

Другими словами, вы можете снаружи вызывать вещи, объявленные внутри, но вы не можете вызывать вещи, объявленные снаружи, находясь внутри.

Код данного приложения написан с учетом зависимостей, как и завещает Clean Architecture.

О функциях


Endpointы каждой функции следующие:

POST: /users
GET: /users
DELETE: /users/:id

Структура директорий


image

Взаимное отображение слоев архитектуры и директорий выглядит так:

  • domain Entities
  • usecase Use Cases
  • interface Controllers Presenters
  • infrasctructure External Interfaces


domain


Директории domain соответствует слой Entity. Так как он находится в самом ядре, вызываться может с любого слоя.

image

src/domain/user.go
package domaintype User struct {    ID   int    `json:"id" gorm:"primary_key"`    Name string `json:"name"`}


Создаем struct User с id и именем и устанавливаем идентификатор в качестве первичного ключа.

Немного о json:id gorm:primary_key:
В json:id json отвечает за маппинг. В gorm:primary_key идет пометка на модели с помощью gorm.
Конечно, помимо primary_key вы можете использовать not null, unique, default и т.д.,

Полезная ссылка объявление моделей на GORM

infrastructure


Самый крайний, внешний слой. Здесь описывается часть, в которой приложение связано с внешним миром. В нашем случае, в этом слое объявляется связь с БД и Router.

image

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

src/infrastucture/sqlhandler.go
package infrastructureimport (    "gorm.io/driver/mysql"    "gorm.io/gorm"    "echoSample/src/interfaces/database")type SqlHandler struct {    db *gorm.DB}func NewSqlHandler() database.SqlHandler {    dsn := "root:password@tcp(127.0.0.1:3306)/go_sample?charset=utf8mb4&parseTime=True&loc=Local"    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})    if err != nil {        panic(err.Error)    }    sqlHandler := new(SqlHandler)    sqlHandler.db = db    return sqlHandler}func (handler *SqlHandler) Create(obj interface{}) {    handler.db.Create(obj)}func (handler *SqlHandler) FindAll(obj interface{}) {    handler.db.Find(obj)}func (handler *SqlHandler) DeleteById(obj interface{}, id string) {    handler.db.Delete(obj, id)}


По поводу баз данных я оставлю ссылки на документацию: gorm.io

Далее идет маршрутизация.
В этом приложении я использую веб-фреймворк Echo. Фреймворк определяет Method и Path API
Ссылка на документацию: https://echo.labstack.com/

src/infrastructure/router.go
package infrastructureimport (    controllers "echoSample/src/interfaces/api"    "net/http"    "github.com/labstack/echo")func Init() {    // Echo instance    e := echo.New()    userController := controllers.NewUserController(NewSqlHandler())    e.GET("/users", func(c echo.Context) error {        users := userController.GetUser()         c.Bind(&users)         return c.JSON(http.StatusOK, users)    })    e.POST("/users", func(c echo.Context) error {        userController.Create(c)        return c.String(http.StatusOK, "created")    })    e.DELETE("/users/:id", func(c echo.Context) error {        id := c.Param("id")        userController.Delete(id)        return c.String(http.StatusOK, "deleted")    })    // Start server    e.Logger.Fatal(e.Start(":1323"))}


interfaces


Слой Controllers Presenters.
Вот здесь уже нужно вспомнить о зависимостях.

image

Нет проблем с вызовом со слоев domain и usecase, но нельзя вызвать слой infrastructure напрямую, поэтому объявим interface. (Получилось немного запутанно, но речь идет про интерфейс sqlHandler, определенный на слое infrastrucure)

src/interfaces/api/user_controller.go
package controllersimport (    "echoSample/src/domain"    "echoSample/src/interfaces/database"    "echoSample/src/usecase"    "github.com/labstack/echo")type UserController struct {    Interactor usecase.UserInteractor}func NewUserController(sqlHandler database.SqlHandler) *UserController {    return &UserController{        Interactor: usecase.UserInteractor{            UserRepository: &database.UserRepository{                SqlHandler: sqlHandler,            },        },    }}func (controller *UserController) Create(c echo.Context) {    u := domain.User{}    c.Bind(&u)    controller.Interactor.Add(u)    createdUsers := controller.Interactor.GetInfo()    c.JSON(201, createdUsers)    return}func (controller *UserController) GetUser() []domain.User {    res := controller.Interactor.GetInfo()    return res}func (controller *UserController) Delete(id string) {    controller.Interactor.Delete(id)}


В controller вызываем со слоёв domain и usecase, поэтому проблем нет.

src/interfaces/api/context.go
package controllerstype Context interface {    Param(string) string    Bind(interface{}) error    Status(int)    JSON(int, interface{})}


Связь с БД

src/interfaces/database/user_repository.go
package databasepackage databaseimport (    "echoSample/src/domain")type UserRepository struct {    SqlHandler}func (db *UserRepository) Store(u domain.User) {    db.Create(&u)}func (db *UserRepository) Select() []domain.User {    user := []domain.User{}    db.FindAll(&user)    return user}func (db *UserRepository) Delete(id string) {    user := []domain.User{}    db.DeleteById(&user, id)}


В repository вызывается sqlHandler, но он вызывается не напрямую со слоя infrastructure, а с помощью объявленного там же interface.
Это называется принципом инверсии зависимостей.

src/interfaces/db/sql_handler.go
package databasetype SqlHandler interface {    Create(object interface{})    FindAll(object interface{})    DeleteById(object interface{}, id string)}


Теперь вы можете вызывать процесс sql_handler.

usecase


Последний оставшийся слой, usecase.

image

src/usecase/user_interactor.go
package usecaseimport "echoSample/src/domain"type UserInteractor struct {    UserRepository UserRepository}func (interactor *UserInteractor) Add(u domain.User) {    interactor.UserRepository.Store(u)}func (interactor *UserInteractor) GetInfo() []domain.User {    return interactor.UserRepository.Select()}func (interactor *UserInteractor) Delete(id string) {    interactor.UserRepository.Delete(id)}


Опять же, нам нужно применить принцип инверсии зависимости, как и раньше. Поэтому определяем user_repository.go.

src/usecase/user_repository.go
package usecaseimport (    "echoSample/src/domain")type UserRepository interface {    Store(domain.User)    Select() []domain.User    Delete(id string)}


На этом реализация завершена.
После этого запустите mysql с docker-compose.yml, запустите сервер, и все должно работать.

docker-compose.ymlversion: "3.6"services:  db:    image: mysql:5.7    container_name: go_sample    volumes:      # настройки mysql      - ./mysql/conf:/etc/mysql/conf.d      - ./mysql/data:/var/lib/mysql    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci    ports:      - 3306:3306    environment:      MYSQL_DATABASE: go_sample      MYSQL_ROOT_PASSWORD: password      MYSQL_USER: root      TZ: "Asia/Tokyo"
Источник: habr.com
К списку статей
Опубликовано: 24.11.2020 14:17:12
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Mysql

Go

Golang

Crud

Clean architecture

Echo

Слоистая архитектура

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru