2020年,Go言語でお世話になったライブラリ/検討したけど導入しなかったライブラリ

この記事は[Wanoグループ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/wano-group)１日目の記事になります。一年早いですね。コロナやリモートワークもあってテンション的に埋まるかどうなのかー。



~~いきなりネタもないので転用できそうなフォーマットで雑多に記事を書きます。~~

本記事では年末っぽく,2020年に自分が使ったGo言語ライブラリ関連のよかったものを一括で振り返えろうかと思います。

* お世話になったライブラリ
* あんまり使わなくなった/検討したけど落ちたライブラリ

この2つの観点で、あまりまとめずつらつら書きました。
単なるライブラリの話題でも、**選定がわかれば問題意識や施策が振り返えれるって気もする**ので、いい機会かなーと思います。


# 1.2020年、お世話になったライブラリ


## Go Generate系
愚直な筋肉の使いどころは考えようってのが2020の施策として多かったので、めんどくさいものをなんとかしてくれるgo generate周りはお世話になりました。

### [github.com/smallnest/gen](https://github.com/smallnest/gen) 

```go
type Admin struct {
	ID int `gorm:"primary_key;AUTO_INCREMENT;column:id;type:uint;" json:"id"`
	CreateTime time.Time `gorm:"column:create_time;type:datetime;default:CURRENT_TIMESTAMP;" json:"create_time"`
	UpdateTime time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP;" json:"update_time"`
	DisplayName null.String `gorm:"column:display_name;type:varchar;size:255;" json:"display_name"`
	LoginName string `gorm:"column:login_name;type:varchar;size:255;" json:"login_name"`
	Password string `gorm:"column:password;type:varchar;size:255;" json:"password"`
	AdminRoleID int `gorm:"column:admin_role_id;type:utinyint;" json:"admin_role_id"`
}
```

ORMとしてGORMを使うことが多いのですが、MySQLの構造体の生成には最近これを使っています。
このgeneratorは生成テンプレートが自由にカスタムできるのが特徴です。

余談ですが、最近は大事なプロジェクトは特に**DBから引っ張ってきたデータをいきなりドメイン上のmodelとして使わず、(O/Rマッピングせず)めんどくても詰め替えと関連レコードの手動取得を行う**という試みをやっています。
このへんは筋肉がいるものの、その愚直な詰め替え作業部分があんまり辛くならないように、このジェネレータでLINQ式っぽいのや各種ヘルパー関数を追加するなど、いろいろと試行錯誤しています。   
  

### [github.com/golang/mock](https://github.com/golang/mock) 
モック生成ツールです。

例えばさくっとaws-sdk-goのs3のmockみたいなのを作るとき、

```go
type s3SdkMock struct {
	s3iface.S3API
	uploadedFiles map[string]bool
}

func (self *s3SdkMock) PutObject(put *s3.PutObjectInput) (*s3.PutObjectOutput, error) {
	self.uploadedFiles[*put.Key] = true
	return &s3.PutObjectOutput{}, nil
}
```
とかでもカンタンですが、あまりにあちこちのテストで使うようだと、interfaceごとまるっと差し替え可能なモックツールにしちゃったほうがいいです。
テスト書く障壁はがんがん下げていきたい。

### [github.com/cheekybits/genny](https://github.com/cheekybits/genny) 
generate支援ツールです。go templateではない、コンパイルエラーにならないgenerateネタがgoで書けます。
このツールで**あらゆる構造体にLINQ式とかを気軽に生やせる環境を整えています**。
愚直に文字列置換するだけなので、ASTが必要なタイプのジェネレートには向きませんが、IDEのコード支援を受けられるのはかなり強く、充分に重宝しています。

```go
import (
	"errors"
	"github.com/cheekybits/genny/generic"
)

// Start ################# KeyTypeSlice ################################

type KeyType generic.Type
type KeyTypeSlice []KeyType

func (rcv KeyTypeSlice) Take(limit int) (result KeyTypeSlice) {

	if len(rcv) < limit {
		return rcv
	}

	return rcv[0:limit]
}

func (rcv KeyTypeSlice) Length() int {
	return len(rcv)
}

//http://golangcookbook.com/chapters/arrays/reverse/
func (rcv KeyTypeSlice) Reverse() KeyTypeSlice {

	for i := len(rcv)/2 - 1; i >= 0; i-- {
		opp := len(rcv) - 1 - i
		rcv[i], rcv[opp] = rcv[opp], rcv[i]
	}

	return rcv
}

func (rcv KeyTypeSlice) Append(list ...KeyType) (result KeyTypeSlice) {
	var old []KeyType = rcv
	newList := append(old, list...)
	return newList
}

func (rcv KeyTypeSlice) Prepend(list ...KeyType) (result KeyTypeSlice) {
	var old []KeyType = rcv
	newList := append(list, old...)
	return newList
}

func (rcv KeyTypeSlice) Tos() (result []KeyType) {
	return ([]KeyType)(rcv)
}


...
```
KeyTypeのところに「VideoGenre」って文字列当てて生成すると,

```go
// Start ################# VideoGenreSlice ################################

type VideoGenreSlice []VideoGenre

// ついでに作った
func (rcv VideoGenreSlice) Take(limit int) (result VideoGenreSlice) {

	if len(rcv) < limit {
		return rcv
	}

	return rcv[0:limit]
}

func (rcv VideoGenreSlice) Length() int {
	return len(rcv)
}

//http://golangcookbook.com/chapters/arrays/reverse/
func (rcv VideoGenreSlice) Reverse() VideoGenreSlice {

	for i := len(rcv)/2 - 1; i >= 0; i-- {
		opp := len(rcv) - 1 - i
		rcv[i], rcv[opp] = rcv[opp], rcv[i]
...
```

```  type VideoGenreSlice  []VideoGenre ```な構造体の出来上がり。

## Log/Error系

### [github.com/golang/xerrors](https://github.com/golang/xerrors)
階層化 Error パッケージです。
一部プロダクトでは優先的に使っており、お世話になりました。

```go
err = xerrors.Errorf("message: %w", err)
```
で作ったエラーは

```go
fmt.Printf("%+v", err)
```

でスタックトレース表示できるようになります。
導入してないプロダクトでは、**スタックトレースというかエラー行数が欲しいためにあちこちにlog.Error()を挟む...** というようなことをやっていましたが、あまりGoっぽくないのもあり、随時これで置換えていきたいと思っています。
スタックトレース機能、なんでGoの仕様から漏れたんだっけ...。



##  画像系
### [github.com/disintegration/imaging](https://github.com/disintegration/imaging)
画像変換をかなり便利にやってくれるライブラリです。依存がないのがいいし、拡大縮小/回転/フィルタ処理/クロッピングまでついています。  
このライブラリのおかげで、imagemagickを使わなきゃいけない場面がかなり減りました。(まだプロジェクトのあちこちには残っているものの)
今は主にAWS Lambdaなどで動かしています。  リアルタイム変換要件もカンタン。


##  Http
###  [github.com/hashicorp/go-retryablehttp](https://github.com/hashicorp/go-retryablehttp)
ただ単にリトライ可能なHTTPクライアントってだけなんですが、あんまり書く機会が多いようだと、このへんで標準化したくなります。
いくつかのプロジェクトで使ったので地味にお世話にはなったんだと思います。


## Config
###  [github.com/joho/godotenv](https://github.com/joho/godotenv)
.envを読みこむツールです。

```go
func main() {
  err := godotenv.Load()
  if err != nil {
    log.Fatal("Error loading .env file")
  }
```

メインのプロダクトでは去年(2019)末くらいから**12 Factor App化**などアプリのモダン化にも注力しており、クラウドのKVSに秘匿情報を一元管理したりする施策を行ってきました。
つまり、アプリ起動後に秘匿情報ファイルを自らとりに行って設定を確定させたり、秘匿情報も書かれたステージごとの設定ファイルを渡すのではなく、なるべく**個別の環境変数によって依存を注入する**方向性です。

ただまあこれってAWS ECSとかコンテナではParameter Storeとかと連係して綺麗に渡せるんですけど、EC2上の既存アプリへの当て方で悩みました。
起動直前のシェル芸が複雑になったり、リスタートやGraceful Shutdownなど、デプロイ上のプロセス管理ツールのライフサイクルとうまく噛み合わず、四苦八苦することになりました。
最終的に**「Go側でも他の言語のように.envくらいは暗黙的に読み込んでいい」** くらいの温度感に落ち着き、このライブラリの選定に至りました。なければ何もしないのでシンプルですね。


# 2. 2020年、あんまり使わなくなった/検討したけど使用に至らなかったライブラリ

### [github.com/spf13/viper](https://github.com/spf13/viper)
設定ファイル読み込みのための人気のライブラリです。12 Factor App化の文脈の一環で調査しました。
[この記事](https://qiita.com/hachi8833/items/fe3d5d6b1285c5ef1821)が詳しいです。

環境変数とyamlの組み合わせなど、設定ファイル構築のためにいろんな手段が取れます。
ただ、**あまりにもAPIが魔術的な気がして**、何度か試したものの採用しませんでした。使いこなせば強力そうですが...。
現行は、せいぜい設定ファイル一枚と秘匿情報系環境変数の組合せ程度で済む感じなので、前述の通りgodotenvと設定ファイル(普通にGoのtemplate)の組み合わせでConfig系を構築しています。


### [github.com/uber-go/zap](https://github.com/uber-go/zap)
ロガーです。
IOをブロックせず速い!らしいので広告案件で使ってました。メソッドが他のロガー系実装と割と解離があったので、interfaceだけechoについている[github.com/labstack/gommon](https://github.com/labstack/gommon)のものに置き換えて、そのアダプタとしてzapを使う...という形をとっていました。
使用感自体はあまり好きではなかったのと、出力をカスタマイズしようとするとそこそこ複雑になったりして、一般的なアプリのプロジェクトだと too much感があり、あんまり周辺の評判はよろしくありませんでした...


### [github.com/casbin/casbin](https://github.com/casbin/casbin)
8200スターを集める認可系フレームワーク。

もともとGo言語だけのものだったんですが、様々な言語対応版があるようです。
様々なアクセス制御モデルに対応しています。
テスト用のEditorもあって、-> ([https://casbin.org/editor](https://casbin.org/editor/))これがなかなかおもしろそうでした。  

**認可系を業務ロジックのコアから切り離す**...という文脈に興味があって調べたんですが、結局こういうツールで永続化層に吐かれるものってスキーマレスであり、ユーザーロール/リソースアクセス認可とかをこれのみで表現するのは管理のコストのが高そう、ということであまり使用に自信が持てませんでした。

綺麗な使い所はどこだろう。


###  [github.com/google/wire](https://github.com/google/wire)
DIツールです。

Dependency Injection自体は意識してやっていこう、というのが2020の方針のひとつでした。
* テスト可能性があがる
* XXXConfigみたいなのをリレーさせず階層毎のコードの責務がすっきりする

~~テスト書く前に疲れちゃうことも多いんですが~~、意味のあるテストを書く、という以上に**個別に小さいユニットテストを書ける単位でモジュールを切る癖**がつくという副次的効果は個人的にもチーム的にも大きかったかな、と思います。

これに限らずDIツールやDIコンテナは現段階ではToo much感があったので採用しませんでしたが、グローバル層というか依存を当てる側(mainパッケージ)は現在進行形で育っているので、いつかこういうツールが欲しくなるものなのかどうか..。



# 2021年
つらつら書いてきて、まだまだやること試したいこと直すこといっぱいなんだなあ、というのがライブラリ選定の振り返りでも見えてきた気がします。

Go言語、来年もひとまずよろしくです。(まだ1ヶ月ある)