LoginSignup
29
28

More than 5 years have passed since last update.

Go言語でDIを試す

Last updated at Posted at 2015-09-05

Go言語でDIを使ってみる。

利用するFW

アプリ説明

下記をリファクタリング
Mithril+golang Gin を試す

コード

  • jsonとDBアクセスで使う構造体 $GOPATH/src/app/model/todo.go
  • リファクタリング前と変化なし。
todo.go
package model

type Todo struct {
    Id int64 `db:"pk" json:"id"`
    Description string `json:"description"`
    Done bool `json:"done"`
}

dbアクセス

  • DBアクセスするコーッドをinterfaceで抽象化する。
  • repository $GOPATH/src/app/repo/repo.go
repo.go
package repo
import "app/model"

type Repo interface{
    FindAll() []model.Todo
    Save(todo *model.Todo)
}

  • DBアクセス用のセットアップ。
  • グローバル変数使わないようにリファクタリング
db.go
package db
import (
    _ "github.com/mattn/go-sqlite3"
    "github.com/naoina/genmai"
    "app/model"
    "github.com/gin-gonic/gin"
    "fmt"
)
func InitDB() *genmai.DB {

    DB, err := genmai.New(&genmai.SQLite3Dialect{}, ":memory:")
    if err != nil {
        panic(err)
    }

    if err := DB.CreateTable(&model.Todo{}); err != nil {
        panic(err)
    }

    initData := []model.Todo{
        {1, "なんかやる", false},
        {2, "なんかやる2", true},
    }
    if _, err = DB.Insert(&initData); err != nil {
        panic(err)
    }

    return DB
}

/**
トランザクション制御のミドルウェア
 */
func  GetTransactionHandlerFunc(DB *genmai.DB) gin.HandlerFunc {
    return func(c *gin.Context) {
        defer func() {
            if err := recover(); err != nil {
                fmt.Println("END TRANSACTION ROLLBACK")
                DB.Rollback()
                panic(err)
            } else {

                DB.Commit()
                fmt.Println("END TRANSACTION COMMIT")
            }
        }()

        fmt.Println("START TRANSACTION")
        if err := DB.Begin(); err != nil {
            panic(err)
        }

        c.Next()

    }
}
  • reposigoryの実装。
repo.go
package db
import (
    "app/model"
    "github.com/naoina/genmai"
)

type RepoDbImpl struct {
    DB *genmai.DB `inject:""`
}

func (r *RepoDbImpl) FindAll() []model.Todo {

    var todos []model.Todo
    if err := r.DB.Select(&todos, r.DB.OrderBy("id", genmai.ASC)); err != nil {
        panic(err)
    }
    return todos
}

func (r *RepoDbImpl) Save(todo *model.Todo) {

    if num, err := r.DB.Update(todo); err != nil {
        panic(err)

    }else if num == 0 {
        if  _, err := r.DB.Insert(todo);err != nil {
            panic(err)
        }
    }

}

  • mainに書いていたコントローラの記述を別パッッケージに切り出す。 $GOPATH/src/app/ctrl/todoCtrl.go
todoCtrl.go
package ctrl
import (
    "app/repo"
    "github.com/gin-gonic/gin"
    "net/http"
    "app/model"
)

type BaseCtrl struct {
    Repo repo.Repo `inject:""`
}

func (base *BaseCtrl) Index(c *gin.Context) {

    c.HTML(http.StatusOK, "index.html", nil)
}

func (base *BaseCtrl) GetTodo(c *gin.Context) {

    c.JSON(http.StatusOK, base.Repo.FindAll())
}

func (base *BaseCtrl) AddTodo(c *gin.Context) {

    var todo model.Todo
    if c.BindJSON(&todo) == nil {

        base.Repo.Save(&todo)
        c.JSON(http.StatusOK, todo)

    }else {
        c.JSON(http.StatusBadRequest, nil)
    }

}


  • mainでは初期化,DI,ルーティングを記述 $GOPATH/src/app/main/app.go
main.go

package main
import (
    "github.com/gin-gonic/gin"
    "app/db"
    "github.com/facebookgo/inject"
    "app/ctrl"
)

func main() {

    r := gin.Default()
    r.Static("/static", "static")
    r.LoadHTMLGlob("templates/*")

    //dbの初期化
    DB:=db.InitDB()
    r.Use(db.GetTransactionHandlerFunc(DB))


    //コントローラのDI
    ctrl := new(ctrl.BaseCtrl)
    if err := inject.Populate(ctrl, new(db.RepoDbImpl),db.DB); err != nil {
        panic(err)
    }

    r.GET("/", ctrl.Index)
    r.GET("/todo", ctrl.GetTodo)
    r.POST("/todo", ctrl.AddTodo)

    r.Run(":9000")

}

  • インデックスページ(htmlテンプレート) $GOPATH/templates/index.html
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/js/mithril.min.js" type="text/javascript" charset="utf-8"></script>
    <title>Mithril TODO</title>
</head>
<body>
<div id="$contents"></div>

<script type="text/javascript">
    //todo component
    var todo = {};
    //model
    todo.Todo = function (data) {
        this.description = m.prop(data.description);
        this.done = m.prop(data.done);
    };

    todo.TodoList = Array;

    //define the view-model
    todo.vm = (function () {
        var vm = {}
        vm.init = function () {
            //a running list of todos
            vm.list = new todo.TodoList();

            //a slot to store the name of a new todo before it is created
            vm.description = m.prop("");

            //init
            m.request({method: "GET", url: "/todo"}).then(function (todoList) {

                todoList.forEach(function (v) {
                    vm.list.push(new todo.Todo({description: v.description, done: v.done}));
                });

            });

            vm.add = function () {
                if (vm.description()) {

                    m.request({
                        method: "POST",
                        url: "/todo",
                        data: {id:t.id,description: vm.description()}
                    }).then(function (t) {

                        vm.list.push(new todo.Todo({id:t.id,description: t.description}));

                        vm.description("");

                    },function(error){
                        console.log("error ");
                    });
                }
            };
            vm.update = function (task) {

                m.request({
                    method: "POST",
                    url: "/todo",
                    data: {id: task.id(),
                           description:task.description(),
                           done:task.done()}
                }).then(function () {
                    console.log("updated");
                }, function (error) {
                    console.log("error ");
                });
            };

        }
        return vm
    }())

    todo.controller = function () {
        todo.vm.init()
    }

    todo.view = function () {
        return m("div", [
            m("input", {onchange: m.withAttr("value", todo.vm.description), value: todo.vm.description(),}),
            m("button", {onclick: todo.vm.add}, "Add"),
            m("table", [
                todo.vm.list.map(function (task, index) {
                    return m("tr", [
                        m("td", [
                            m("input[type=checkbox]",
                               {
                               onclick:function (e) {
                                    task.done(e.target.checked)
                                    todo.vm.update(task)
                                }
                               ,checked: task.done()
                               })
                        ]),
                        m("td", {style: {textDecoration: task.done() ? "line-through" : "none"}}, task.description()),
                    ])
                })
            ])
        ]);
    };
    m.mount($contents, {controller: todo.controller, view: todo.view});
</script>
</body>
</html>

  • mithrilの置き場 $GOPATH/static/mithril.js

実行

rerun app/main

感想

かなりすっきりした。

29
28
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
29
28