uber-go/fxを触りました。

## はじめに
こんにちは、昨日に引き続きMedia Do Advent Calendar 3日目を担当するおかざわです。

[uber-go/digの記事](https://qiita.com/okzw/private/06b4c93714a6e8fd3fb6)と同じく、[ubar-go/fx](https://github.com/uber-go/fx)を勉強を勉強する機会がありましたのでその時のまとめになります。
今回は短いですが、昨日と同じく暖かい目で見てやってください。

## 概要
> Package fxは、再利用可能で構成可能なモジュールからアプリケーションを簡単に構築できるフレームワークです。
Fxアプリケーションは依存関係注入を使用して、関数呼び出しを手動でつなぐ面倒な作業を行うことなくグローバルを排除します。依存性注入に対する他のアプローチとは異なり、Fxは単純なGo関数で動作します。構造体タグを使用したり、特殊な型を埋め込む必要がないため、FxはほとんどのGoパッケージで自動的に動作します。

[uber-go/fxのdoc](https://godoc.org/go.uber.org/fx)のgoogle翻訳より

やはり、ぱっと見では何をしてくれるものなのかよくわからないですが、以下の部分から[uber-go/dig](https://github.com/uber-go/dig)と同じ様なことをすることが伺えます。（実際uber-go/fxはuber-go/digを内部で使用している）

>Fxアプリケーションは依存関係注入を使用して、関数呼び出しを手動でつなぐ面倒な作業を行うことなくグローバルを排除します。

実際に使ってみて何をするのかを調べます。

## インストール

```
go get go.uber.org/fx@v1
```

go modulesをしている前提のインストールです。
それ以外は[ubar-go/fx](https://github.com/uber-go/fx)を参照ください。

## 使う

### 比較

[uber-go/digを触りました。](https://qiita.com/okzw/items/06b4c93714a6e8fd3fb6)で使用したコードを使ってfxと見比べてみます。

``` go
package main

// 通常とdigとfxの比較

import (
	"fmt"

	"go.uber.org/dig"
	"go.uber.org/fx"
)

type Usecase interface {
	Use()
}

type Repository interface {
	RepoPrint()
}

type usecase struct {
	repo Repository
}

func NewUsecase(r Repository) Usecase {
	return &usecase{
		repo: r,
	}
}
func (u usecase) Use() {
	u.repo.RepoPrint()
}

type repository struct{}

func NewRepository() Repository {
	return &repository{}
}

func (r repository) RepoPrint() {
	fmt.Println("1")
}

func main() {

	// 通常の依存関係注入
	repo := NewRepository()
	usecase := NewUsecase(repo)
	usecase.Use()

	// uber-go/digを使った依存関係注入
	c := dig.New()
	c.Provide(NewUsecase)
	c.Provide(NewRepository)
	c.Invoke(func(u Usecase) {
		u.Use()
	})

	// uber-go/fxを使った依存関係注入
	// 中身ではuber-go/digが使われている（なのでdigでできることはだいたいできる）
	app := fx.New(
		fx.Provide(
			NewUsecase,
			NewRepository,
		),
		fx.Invoke(func(u Usecase) {
			u.Use()
		}),
	)
	// 完了する
	app.Done()
}
```

main()の部分で通常のDI、digを使ったDI、fxを使ったDIを見比べることができます。
digではコンテナを生成し、コンテナに対して、`Provide()`や`Invoke()`をしますが、
fxではappを生成するタイミングで`Provide()`、`Invoke()`を行っています。
また、生成したappは`app.Done()`を行って処理を終了しています。

### fxの機能

昨日のuber-go/digの説明をしていた時は`Invoke()`で処理を実行して終了するものを扱うのが前提でした。
uber-go/fxも同じく`Invoke()`で処理を実行しますが、生成したappを「実行」することができます。

これを説明するためにはhttpサーバ の様な「起動」と「終了」を明確に実行できるアプリケーションを作るのがわかりやすいです。

```go
package main

// Start&StopとRunの活用

import (
	"context"
	"fmt"
	"log"
	"net/http"

	"go.uber.org/fx"
)

func NewMux(lc fx.Lifecycle) *http.ServeMux {
	log.Print("Executing NewMux.")
	mux := http.NewServeMux()
	server := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}
	lc.Append(fx.Hook{
		OnStart: func(context.Context) error {
			log.Print("Starting HTTP server.")
			go server.ListenAndServe()
			return nil
		},
		OnStop: func(ctx context.Context) error {
			log.Print("Stopping HTTP server.")
			return server.Shutdown(ctx)
		},
	})

	return mux
}

func Register(mux *http.ServeMux) {
	// 「/」の設定
	log.Println("Create Handler Default World")
	mux.HandleFunc("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "Default World")
		log.Println("Passing Default World")
	}))
	// 「/hello」の設定
	log.Println("Create Handler Hello World")
	mux.HandleFunc("/hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, "Hello World")
		log.Println("Passing Hello World")
	}))
}

func main() {
	app := fx.New(
		fx.Provide(
			NewMux,
		),
		fx.Invoke(Register),
	)
	app.Run()
}
```

`Invoke()`にはマルチプレクサの登録をする関数が指定されており、その関数を実行するために`*http.ServeMux`を取得する必要がある。
（マルチプレクサ？ってなる人は → https://qiita.com/huji0327/items/c85affaf5b9dbf84c11e ）
`Provide()`内で`*http.ServeMux`を返す関数である`NewMux`が指定されているため実行される。
サーバ の設定を`NewMux`から取得し、マルチプレクサの登録をする。

上記が`Invoke()`内でする処理である。

`app.Run()`は`NewMux`の引数に設定されている`fx.Lifecycle`の処理を実行する。
（以降`fx.Lifecycle`をライフサイクルと呼びます）

```go
lc.Append(fx.Hook{
        OnStart: func(context.Context) error {
            log.Print("Starting HTTP server.")
            go server.ListenAndServe()
            return nil
        },
        OnStop: func(ctx context.Context) error {
            log.Print("Stopping HTTP server.")
            return server.Shutdown(ctx)
        },
    })
```

`OnStart`はアプリケーション実行時に処理され、
`OnStop`はアプリケーション終了時に処理される。

上記の処理によりサーバ の起動と終了処理を実行することができる。

もし、複数ライフサイクルに処理が紐付けされていた場合、`Invoke()`するstructの順番がそのままライフサイクルのstartの順番、stopは逆の順番で処理されます。

### 実行方法

appを実行する方法は大きく分けて３種類あります。

`app.Done()`はappで作成したアプリケーションを実行しない時に使用します。
終了のシグナルを出し、アプリケーションを終了します。

```
app.Done()
```

`app.Start()`と`app.Stop()`は明確にStartの処理とStopの処理を実行させたい時に使用します。
goのhttpサーバ だと、`http.Get()`などを用いて自動で通信させたりする時に重宝します。

```
app.Start(/*コンテキスト*/)
app.Stop(/*コンテキスト*/)
```

`app.Run()`は内部で`run(Done)`しており、`run()`は上記の`Start()+Stop()`を持っています。
`app.Run()`実行時点で`Start()`の処理が走り、終了のシグナル（control + c）を受信後`Stop()`の処理を実行します。

```
app.Run()
```

fxはdigとは違いアプリケーションの実行周りも管理することができるライブラリです。

### その他機能

<details>
<summary>dig.Inを活用したfxのサンプル</summary>
<div>

```go
package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"time"

	"go.uber.org/fx"
)

func NewLogger() *log.Logger {
	logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)
	logger.Print("Executing NewLogger.")
	return logger
}

func NewDefaultHandler(logger *log.Logger) (http.Handler, error) {
	logger.Print("Executing NewDefaultHandler.")
	return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		logger.Print("Default World")
	}), nil
}

func NewHelloHandler(logger *log.Logger) (http.Handler, error) {
	logger.Print("Executing NewHelloHandler.")
	return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		logger.Print("Hello World")
	}), nil
}

func NewMux(lc fx.Lifecycle, logger *log.Logger) *http.ServeMux {
	logger.Print("Executing NewMux.")
	mux := http.NewServeMux()
	server := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}
	lc.Append(fx.Hook{
		OnStart: func(context.Context) error {
			logger.Print("Starting HTTP server.")
			go server.ListenAndServe()
			return nil
		},
		OnStop: func(ctx context.Context) error {
			logger.Print("Stopping HTTP server.")
			return server.Shutdown(ctx)
		},
	})

	return mux
}

type inHandle struct {
	fx.In

	DefaultH http.Handler `name:"default"`
	HelloH   http.Handler `name:"hello"`
}

type setHandle struct {
	defaultH http.Handler
	helloH   http.Handler
}

func Register(mux *http.ServeMux, h *setHandle) {
	mux.Handle("/", h.defaultH)
	mux.Handle("/hello", h.helloH)
}

func main() {
	app := fx.New(
		fx.Provide(
			NewLogger,
			NewMux,
			// fx.Annotated{}は登録するfuncに対して名前をつけることができる
			// この挙動はfx.Outのstructを作成することでもできる
			fx.Annotated{
				Name:   "default",
				Target: NewDefaultHandler,
			},
			fx.Annotated{
				Name:   "hello",
				Target: NewHelloHandler,
			},
			func(h inHandle) *setHandle {
				return &setHandle{
					defaultH: h.DefaultH,
					helloH:   h.HelloH,
				}
			},
		),
		fx.Invoke(Register),
	)

	// app.Run()

	startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	if err := app.Start(startCtx); err != nil {
		log.Fatal(err)
	}

	http.Get("http://localhost:8080/")
	http.Get("http://localhost:8080/hello")

	stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	if err := app.Stop(stopCtx); err != nil {
		log.Fatal(err)
	}
}
```

</div>
</details>

上記のサンプルのmain()のProvide()の様に

```go
fx.Annotated{
	Name:   "default",
	Target: NewDefaultHandler,
},
```

と記述することでdig.Outを作らずともNameオプションやgroupオプションを設定することができます。

## 感想
uber-go/digを事前に勉強していたためそこまでつまずいたところはなかったですが、golangのhttpサーバの関連のstructについてやインターフェースの構造など、別のところの知識が不足していて躓くことが多かったです。
それと参考文献が本当にないのでサンプルを作るのが大変だった。
以上。

### 参考文献

doc:https://godoc.org/go.uber.org/fx
github:https://github.com/uber-go/fx
https://journal.lampetty.net/entry/understanding-http-handler-in-go
https://qiita.com/huji0327/items/c85affaf5b9dbf84c11e