Help us understand the problem. What is going on with this article?

Go言語でDIを試す

More than 3 years have passed since last update.

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

感想

かなりすっきりした。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした