Golang を使った依存性注入のやり方


Golang を使ってどのように依存性を注入するのか

やり方

  1. main.go を作成する
  2. 依存対象のパッケージを作成する
  3. interface, struct, interface を返却するNew関数を作成する
  4. 手順3 で作成したNew関数を呼び出す

1. main.go を作成する

cmd/app/main.go を作成し、プログラム実行の入口を用意します

main.go
package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}

2. 依存対象のパッケージを作成する

今回はクリーンアーキテクチャを意識して、usecase 層を作成します
cmd ディレクトリと同じ階層に usecase ディレクトリを作成し、適当なgo ファイルを作成します。
今回はアプリユーザーに関するusecase を設定すると仮定し、user.go を作成します。

user.go
package usecase

// TODO 今後の手順で処理を追加する

3. interface, struct, interface を返却するNew関数を作成する

user 関連のロジックを決め、interface, struct, New関数 を作成していきます。
今回は簡単のために何かしらの返答を行う機能を持つということにし、user.go に追記していきます。

interface を定義

user.go
type User interface {
    Answer() (string, error)
}

struct を作成

user.go
type User interface {
    Answer() (string, error)
}

type user struct {
	// もしuser の持つロジックが別のパッケージに依存する場合はここに記述
}

レシーバ関数でロジックを実装

構造体のレシーバ関数としてAnswer メソッドを実装することで、User interface に定義されたAnswer メソッドを実装したことになる

user.go
type User interface {
    Answer() (string, error)
}

type user struct {
	// もしuser の持つロジックが別のパッケージに依存する場合はここに記述
}

func (receiver *user) Answer() (string, error) {
	return "Answerメソッドが呼ばれました", nil
}

New関数を実装

user インターフェースを返却する関数を実装します。
この関数を呼び出すことで、Answer メソッドが実装されたインターフェースをmain.go で使用できるようになります。

user.go
func NewUserUsecase() User {
	return &user{}
}

type User interface {
    Answer() (string, error)
}

type user struct {
	// もしuser の持つロジックが別のパッケージに依存する場合はここに記述
}

func (receiver *user) Answer() (string, error) {
	return "Answerメソッドが呼ばれました", nil
}

4. main.go でNew関数を呼び出す

main.go
package main

import "fmt"

func main() {
	userUsecase, nil := usecase.NewUserUsecase()
	fmt.Println(userUsecase.Answer()) // "Answerメソッドが呼ばれました"
}

おわりに

以上で依存性の注入ができたことになります。
main.go から usecase.go への依存は、user構造体の持つロジックではなく、User インターフェースに向いています。
User インターフェースに定義されているAnswer関数を呼び出したら、たまたまその関数を実装していたuser構造体のAnswerメソッドのロジックが呼び出された。 という形になります。