はじめに
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
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の場合と変わらないので省略します。
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層
から呼び出します。
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を利用することで割とすっきりと書くことができました。後の部分は参考と同じ実装で問題ないので省略します。
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環境簡単構築