LoginSignup
52
47

More than 3 years have passed since last update.

Golang - EchoとGORMでClean Architecture APIを構築する

Posted at

はじめに

Goのフレームワークを最近触り出して、アーキテクチャについて学んだ方がいいなと感じていた所、下記の記事に出会いAPIサーバーを構築してみることにしました。アーキテクチャのアの字も知らなかったので大変参考になりました。

Clean ArchitectureでAPI Serverを構築してみる

せっかくなので以下の二点変更点を加えて構築していきます。

  • フレームワークをGinからEchoに変更
  • ORM(GORM)を導入

サンプルはこちらのリポジトリにあげてありますので宜しければ参照ください。

構成

Dockerを利用して、APIとMySQLをそれぞれ用意しました。

└── golang_echo_example
    ├── api
    │   ├── domain
    │   ├── infrastructure
    │   ├── interfaces
    │   │   ├── controllers
    │   │   └── database
    │   ├── server.go
    │   └── usecase
    ├── docker
    │   ├── api
    │   │   └── Dockerfile
    │   └── mysql
    │       └── Dockerfile
    ├── docker-compose.yml
    └── mysql
        ├── conf.d
        └── initdb.d

Echoを利用したルーティングの実装

Frameworks & Drivers層

LoggerとRecoverのミドルウェアを有効にします。エンドポイントは以下のものを実装しました。

  • ユーザー情報取得:GET /user/:id
  • ユーザー一覧取得:GET /users
  • ユーザー情報登録:POST /user/:id
  • ユーザー情報更新:PUT /user/:id
  • ユーザー情報削除:DELETE /user/:id
infrastructure/router.go
package infrastructure

import (
    "net/http"

    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
    "github.com/so-heee/golang_echo_example/api/interfaces/controllers"
)

func Init() {
    // Echo instance
    e := echo.New()

    userController := controllers.NewUserController(NewSqlHandler())

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    e.GET("/", hello)
    e.GET("/users", func(c echo.Context) error { return userController.Index(c) })
    e.GET("/users/:id", func(c echo.Context) error { return userController.Show(c) })
    e.POST("/create", func(c echo.Context) error { return userController.Create(c) })
    e.PUT("/users/:id", func(c echo.Context) error { return userController.Save(c) })
    e.DELETE("/users/:id", func(c echo.Context) error { return userController.Delete(c) })

    // Start server
    e.Logger.Fatal(e.Start(":1323"))
}

Interfaces/controllers層

各エンドポイントごとにコントローラーを実装します。
ここからはGinの場合と変わらないので省略します。

interfaces/controllers/user_controller.go
func (controller *UserController) Create(c echo.Context) (err error) {
    u := domain.User{}
    c.Bind(&u)
    user, err := controller.Interactor.Add(u)
    if err != nil {
        c.JSON(500, NewError(err))
        return
    }
    c.JSON(201, user)
    return
}

ORM(GORM)を利用したMySQL操作

Frameworks & Drivers層

infrastructureレイヤーにデータベース接続を実装しています。gormをimportしてデータベースとの接続を行います。またGORMのCRUD Interface単位でメソッドを定義し、interfaces/database層から呼び出します。

infrastructure/sqlhandler.go
package infrastructure

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"

    "github.com/so-heee/golang_echo_example/api/interfaces/database"
)

type SqlHandler struct {
    Conn *gorm.DB
}

func NewSqlHandler() database.SqlHandler {
    conn, err := gorm.Open("mysql", "root:root@tcp(mysql)/sample?charset=utf8&parseTime=True&loc=Local")
    if err != nil {
        panic(err.Error)
    }
    sqlHandler := new(SqlHandler)
    sqlHandler.Conn = conn
    return sqlHandler
}

func (handler *SqlHandler) Find(out interface{}, where ...interface{}) *gorm.DB {
    return handler.Conn.Find(out, where...)
}

func (handler *SqlHandler) Exec(sql string, values ...interface{}) *gorm.DB {
    return handler.Conn.Exec(sql, values...)
}

func (handler *SqlHandler) First(out interface{}, where ...interface{}) *gorm.DB {
    return handler.Conn.First(out, where...)
}

func (handler *SqlHandler) Raw(sql string, values ...interface{}) *gorm.DB {
    return handler.Conn.Raw(sql, values...)
}

func (handler *SqlHandler) Create(value interface{}) *gorm.DB {
    return handler.Conn.Create(value)
}

func (handler *SqlHandler) Save(value interface{}) *gorm.DB {
    return handler.Conn.Save(value)
}

func (handler *SqlHandler) Delete(value interface{}) *gorm.DB {
    return handler.Conn.Delete(value)
}

func (handler *SqlHandler) Where(query interface{}, args ...interface{}) *gorm.DB {
    return handler.Conn.Where(query, args...)
}

Interfaces/database層

実際にMySQLとのやり取りを実装していきます。GORMのQueryを利用することで割とすっきりと書くことができました。後の部分は参考と同じ実装で問題ないので省略します。

interfaces/database/user_repository.go
func (repo *UserRepository) FindById(id int) (user domain.User, err error) {
    if err = repo.Find(&user, id).Error; err != nil {
        return
    }
    return
}

動作確認

APIとMySQLのコンテナを起動します。

// コンテナの立ち上げ
$ docker-compose up -d

// MySQLの初期化スクリプトを実行
$ ./init-mysql

localhost:1323/usersでユーザー一覧が無事取得できました。

$ curl -i -H 'Content-Type:application/json' localhost:1323/users                                              
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Fri, 25 Oct 2019 06:19:20 GMT
Content-Length: 249

[{"ID":1,"FirstName":"Patricia","LastName":"Smith"},{"ID":2,"FirstName":"Linda","LastName":"Johnson"},{"ID":3,"FirstName":"Mary","LastName":"William"},{"ID":4,"FirstName":"Robert","LastName":"Jones"},{"ID":5,"FirstName":"James","LastName":"Brown"}]

参考

Clean ArchitectureでAPI Serverを構築してみる
echo 公式ガイド
docker-compose でMySQL環境簡単構築

52
47
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
47