Goでレイヤードアーキテクチャ

# レイヤードアーキテクチャとは
APIサーバを実装するときはだいたい以下の流れだと思います．
> データを受け取って -> 処理して -> データベースに保存

レイヤードアーキテクチャは簡単に言うと，この流れを**責務に乗っ取り分割**することで，**依存関係を少なく**し，**メンテナンスのしやすい構成**にしようと言う考え方になります．
## 構成
![image.png](https://qiita-image-store.s3.amazonaws.com/0/192927/a1d81d9b-918e-acdb-987f-56598711e5f5.png)

- UI(Presentation)層: クライアントのことを指します． APIサーバでは考えません．
- Application層: Domain層を用いて，クライアントが欲しいデータを返すのが責務です．
- Domain層: クライアントからのデータを処理するのが責務です．
- Infrastructure層: DBとの通信が責務です．

# 題材
https://github.com/tozastation/gRPC-Training-Golang
を使って説明していきます
## 構成
```
├── domain: ドメイン層
│   ├── repository: 依存性逆転の原則
│   └── service: ロジック
├── idl: Protocol Bufferの定義ファイル
├── implements: アプリケーション層
├── infrastructure: インフラ層
│   └── persistence
│       ├── model: DBモデル
│       └── mssql: mssqlサーバ用Repository
├── interfaces: その他
│   ├── auth: 認証
│   ├── di: 依存性の注入
│   ├── handler: ハンドラー
│   └── rpc: gRPC生成ファイル
├── main.go
├── protoc.sh
└── vendor: Goのパッケージ
```

### ドメイン層

#### repository
repositoryでは，インフラ層におけるrepositoryのインターフェースを作成します．
##### 依存性逆転の原則(抽象に依存せよ)
先ほど説明したレイヤードアーキテクチャだけでは困ることがあります．それは，**上位レイヤが下位レイヤの実体を持っている必要があること**です．
下位レイヤの実装が変更されると，上位レイヤは影響を受けることや，上位レイヤは下位レイヤの実装後でないと参照できないなどの不便さが残ります．
そのため，interfaceを用いて，抽象に依存します．これにより，上位レイヤは下位レイヤの詳細は知らずとも良くなります．

##### 実装

```Go
package repository

// IUserRepository is ...
type IUserRepository interface {
	FindUserByUserToken(ctx context.Context, token string) (*db.User, error)
	CreateUser(user *db.User) (string, error)
	Login(uID string, password []byte) (string, error)
}
```

#### service
##### 実装
1. アプリケーション層がサービス層を参照するためにインターフェースを定義しています．
2. インフラ層のインターフェースに依存した構造体を定義しています．
3. 構造体を生成するメソッドを定義しています(のちに解説しますがこれが**DI**と呼ばれるものです．)
4. 定義したインターフェースに合わせてメソッドを実装します．

```Go
package service

// IUserService ...
type IUserService interface {
	GetMe(ctx context.Context, token string) (*rpc_user.GetUser, error)
	SignIn(ctx context.Context, uID, password string) (string, error)
	SignUp(ctx context.Context, user *rpc_user.PostUser) (string, error)
}

type userService struct {
	irepo.IUserRepository
}

// NewUserService is ...
func NewUserService(repo irepo.IUserRepository) IUserService {
	return &userService{repo}
}

func (srv *userService) GetMe(ctx context.Context, token string) (*rpc_user.GetUser, error) {
	user, err := srv.IUserRepository.FindUserByUserToken(ctx, token)
	if err != nil {
		return nil, err
	}
	return dbToPostUser(user), nil
}

func dbToPostUser(user *db.User) *rpc_user.GetUser {
	return &rpc_user.GetUser{
		Name:     user.Name,
		CityName: user.CityName,
	}
}
```

### アプリケーション層
#### implement
##### 実装
Application層が依存するもの無くない？と思われると思います．
これはGoの特徴なのですが，DIを行うメソッドを定義した際に，**インターフェースを返り値**として実装しています．
このようにすることで，**定義したインターフェースの内容を満たさないと関数が実装されたとコンパイラが認めない**ため，この構造体は定義したメソッドを全て含んでいるということを担保することができます．

```Go
package implements

// IUserImplement is ...
type IUserImplement interface {
	Get(ctx context.Context, p *rpc_user.GetRequest) (*rpc_user.GetResponse, error)
	Login(ctx context.Context, p *rpc_user.LoginRequest) (*rpc_user.LoginResponse, error)
	Post(ctx context.Context, p *rpc_user.PostRequest) (*rpc_user.PostResponse, error)
}

type userImplement struct {
	isrv.IUserService
	*logrus.Logger
}

// NewUserImplement is ...
func NewUserImplement(s isrv.IUserService, l *logrus.Logger) IUserImplement {
	return &userImplement{s, l}
}

func (imp *userImplement) Get(ctx context.Context, p *rpc_user.GetRequest) (*rpc_user.GetResponse, error) {
	imp.Logger.Infoln("[START] GetMyBoughtVegetablesRPC is Called from Client")
	token := p.GetToken()
	user, err := imp.IUserService.GetMe(ctx, token)
	if err != nil {
		return nil, err
	}
	res := rpc_user.GetResponse{
		User: user,
	}
	imp.Logger.Infoln("[END] GetMyBoughtVegetablesRPC is Called from Client")
	return &res, nil
}
```

###インフラ層
#### persistence
責務はデータ永続化です
##### model

###### 実装
DBに受け渡すデータを構造体として定義します

```Go
package db

// User is ...
type User struct {
	BaseModel
	Name        string
	CityName    string
	Password    []byte
	AccessToken string
}
```
##### mssql

###### 実装
特徴としては，database/sqlを用いて，SQL文を直書きしています．
理由としては，`orm`は独自の構文を使用するため他ライブラリの以降にコストがかかりますが，SQL文だと汎用性があるためです．

```Go
package mssql

// UserRepository is
type UserRepository struct {
	*sql.DB
}

// NewUserRepository is ...
func NewUserRepository(Conn *sql.DB) irepo.IUserRepository {
	return &UserRepository{Conn}
}

// FindUserByUserToken is ...
func (repo *UserRepository) FindUserByUserToken(ctx context.Context, token string) (*db.User, error) {
	dbUser := db.User{}
	if err := repo.DB.QueryRow("SELECT CityName FROM [Weather].[dbo].[Users] WHERE AccessToken = " + token).Scan(&dbUser.CityName); err != nil {
		return nil, err
	}
	return &dbUser, nil
}
```

### その他
#### auth
JWT(Json Web Token)という技術を用いています．興味ある方は[こちらを参照](https://qiita.com/gctoyo/items/8d0ffb265845ab8cc87c)
#### di
##### 依存性注入(Dependency Injection)
先ほどからDIDIと言ってきましたが，DIとは外部からクラスを射し込むことを言います．
##### 実装
テストを書くときを考えてみてください．
サービス層のテストをするとなると，DBの情報が欲しいとなります．
となるとリポジトリが必要そうです．しかしこれでは，DBに依存してしまい切り分けた意味がなくなってしまいます．
DIをすることにより，決まった値を返すモックのリポジトリを注入することができ，晴れて依存性を解消できるという訳です．

```Go
package di

var logger = logrus.New()

// InitializeUser is ...
func InitializeUser() implements.IUserImplement {
	repo := mssql.NewUserRepository(handler.OpenDBConnection())
	srv := service.NewUserService(repo)
	imp := implements.NewUserImplement(srv, logger)
	return imp
}
```
#### handler
DBなどのコネクションを生成するメソッドを集めています．
#### rpc
gRPCの説明は後に回します