Go言語とClean ArchitectureでAPIサーバを構築する

# Clean Architectureとは
Clean Architecture(クリーンアーキテクチャ)とは，レイヤに内側と外側の関係性を持たせるアーキテクチャである．
「外側のレイヤは内側のレイヤだけに依存する」というルールを守ることによって，**アプリケーションから技術を分離すること**が目的である．
## アプリケーションから技術を分離すると何が嬉しいのか
ここでの「技術」とは，HTTPやcsv，MySQLなどのことを意味している．
アプリケーションから技術を分離すると，技術を容易に変更できたり，テストコードを書くときに容易にモックできたりする．
例えば，出力をHTTPからcsvに変更したくなったときなどに容易に変更が可能である．
# 各レイヤの責務
Clean Architectureで提案されているレイヤ構造は以下の画像のようなものである．
![CleanArchitecture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376846/8e040b9e-0385-7b9f-558e-9f0744a87e53.jpeg)
内側から，Entitiesレイヤ，Use Casesレイヤ，Interface Adaptersレイヤ，Frameworkd & Driversレイヤの4つのレイヤから構成される．
「外側のレイヤは内側のレイヤだけに依存する」というルールが存在し，例えば，Use CasesレイヤがExternal Interfacesレイヤに依存するようなことがあってはならない．
また，技術に依存しているコードを置いていいのはInterface Adaptersレイヤ，Frameworkd & Driversレイヤの外側2層だけで，Entitiesレイヤ，Use Casesレイヤには技術に依存したコードをおいてはならない．

各レイヤの責務を大まかに説明すると次のようなものである．
## Entitiesレイヤ
ドメインロジックを実装する責務を持つ．
DB操作などの技術的な実装を持ってはならない．
また，他のどのレイヤにも依存してはならない．
## Use Casesレイヤ
Entitiesレイヤのオブジェクトを操作してビジネスロジックを実行する責務を持つ．
さらに，このレイヤにはポートを定義する．ここで，ポートとは，アダプターで実装を差し替えることができる対象のことである．
Go言語の場合，ポートはInterfaceにあたる．
InputPort・OutputPortはそれぞれ，入力・出力に関するポート，
## Interface Adaptersレイヤ
Use Casesレイヤで定義したポートに対する実装を提供する．すなわち，InterfaceのMethodを定義する(実態を作ると考えるとよい)．
それゆえ，このレイヤでDB操作やHTTP入出力などの技術的な実装を定義する．
Controllersは入力に関するアダプター，Presentersは出力に関するアダプター，Gatewaysは永続化に関するアダプターである．
## Frameworks & Driversレイヤ
DBのconnection生成やroutingなどの技術的な実装をおく．
# Go言語を用いてAPIサーバを構築する．
ここで作成するAPIはPathParameterからuserIDを受け取り，そのuserIDをもつuserの名前をDBから取得し，出力するものである．

```test
GET /user/:id
input: userID string
output: userName string
```
以下で出てくるコードは全て [サンプルコード](https://github.com/arkuchy/clean-architecture-sample) においてある．

## package構成
大まかなpackage構成が以下である．

```bash
.
├── adapter
│   ├── controller
│   │   └── user.go
│   ├── gateway
│   │   └── user.go
│   └── presenter
│       └── user.go
├── driver
│   └── user.go
├── entity
│   └── user.go
└── usecase
    ├── interactor
    │   └── user.go
    └── port
        └── user.go
```

## package同士の関係
package同士の関係は以下の画像のとおりである．ただし，重要な部分だけを抜き出している．
実線は依存(使用していると読み替えても良い)，点線は実装(Interfaceを満たすようにMethodを定義)を表している．
![architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376846/a70baa46-4ae2-5340-40d8-63c687351ef4.png)

大まかな流れとしては，以下のようなものである．

1. `driver`から`adapter/controller`を呼び出す
1. `adapter/controller`は，ポートを全て組み立てて，`usecase/port/inputPort`を実行する
1. `usecase/port/inputPort`は`usecase/interactor`が実装しているので，`usecase/interactor`のMethodが呼ばれる
1. `usecase/interactor`では，`entity`のドメインロジックを実行する．
1. `usecase/interactor`では，`usecase/port/userRepository`を呼び出し，DBの永続化処理を行う(`usecase/port/userRepository`は`adapter/gateway`が実装しているので，`adapter/gatewayのMethod`が呼ばれる)
1. `usecase/interactor`では，`usecase/port/outputPort`を呼び出し，出力を行う(`usecase/port/outputPort`は`adapter/presenter`が実装しているので，`adapter/presenter`のMethodが呼ばれる)

通常のMVCなどでは，`controller`が入力を受け取り，`model`を呼び出しドメインロジックを実行し，`controller`が出力を行うが，Clean Architectureでは，入力は`inputPort`，出力は`outputPort`が担当していることに注意する．それゆえ，`adapter/controller`では，単に`usecase/port/inputPort`を実行するだけで，返り値を受け取ったり出力を行ったりする必要はない．

## サンプルコード
次に[サンプルコード](https://github.com/arkuchy/clean-architecture-sample)を読みながらClean Architectureの流れを理解する．

1． `driver`から`adapter/controller`を呼び出す

```go:driver/user.go
package driver

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"os"

	// blank import for MySQL driver
	"github.com/arkuchy/clean-architecture-sample-sample/adapter/controller"
	"github.com/arkuchy/clean-architecture-sample-sample/adapter/gateway"
	"github.com/arkuchy/clean-architecture-sample-sample/adapter/presenter"
	"github.com/arkuchy/clean-architecture-sample-sample/usecase/interactor"
	_ "github.com/go-sql-driver/mysql"
)

// Serve はserverを起動させます．
func Serve(addr string) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DATABASE"))
	conn, err := sql.Open("mysql", dsn)
	if err != nil {
		log.Println(err)
		return
	}
	user := controller.User{
		OutputFactory: presenter.NewUserOutputPort,
		InputFactory:  interactor.NewUserInputPort,
		RepoFactory:   gateway.NewUserRepository,
		Conn:          conn,
	}
	http.HandleFunc("/user/", user.GetUserByID)
	err = http.ListenAndServe(addr, nil)
	if err != nil {
		log.Fatalf("Listen and serve failed. %+v", err)
	}
}
```
`driver/user.go`ではDBのconnectionを生成し，routingの設定を行なっている．
`adapter/controller/user.go`で定義されている`controller.User`を作成し，`http.HandleFunc()`に`controller.User.GetUserByID`を渡している
<br>
2． `adapter/controller`は，ポートを全て組み立てて，`usecase/port/inputPort`を実行する

```go:adapter/controller/user.go
package controller

import (
	"database/sql"
	"net/http"
	"strings"

	"github.com/arkuchy/clean-architecture-sample-sample/usecase/port"
)

type User struct {
	OutputFactory func(w http.ResponseWriter) port.UserOutputPort
	// -> presenter.NewUserOutputPort
	InputFactory func(o port.UserOutputPort, u port.UserRepository) port.UserInputPort
	// -> interactor.NewUserInputPort
	RepoFactory func(c *sql.DB) port.UserRepository
	// -> gateway.NewUserRepository
	Conn *sql.DB
}

// GetUserByID は，httpを受け取り，portを組み立てて，inputPort.GetUserByIDを呼び出します．
func (u *User) GetUserByID(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	userID := strings.TrimPrefix(r.URL.Path, "/user/")
	outputPort := u.OutputFactory(w)
	repository := u.RepoFactory(u.Conn)
	inputPort := u.InputFactory(outputPort, repository)
	inputPort.GetUserByID(ctx, userID)
}
```
1で呼ばれた`controller.User.GetUserByID`内では，入力を受け取りPathParmeterを取得した後，全てのポート(`UserInputPort`, `UserOutputPort`, `UserRepository`)を組み立てて，`inputPort.GetUserByID`を呼び出す．
<br>
3． `usecase/port/inputPort`は`usecase/interactor`が実装しているので，`usecase/interactor`のMethodが呼ばれる

```go:usecase/port/user.go
package port

import (
	"context"

	"github.com/arkuchy/clean-architecture-sample-sample/entity"
)

type UserInputPort interface {
	GetUserByID(ctx context.Context, userID string)
}

type UserOutputPort interface {
	Render(*entity.User)
	RenderError(error)
}

// userのCRUDに対するDB用のポート
type UserRepository interface {
	GetUserByID(ctx context.Context, userID string) (*entity.User, error)
}
```
`usecase/port/user.go`に定義されている`UserInputPort`はInterfaceなので，このInterfaceを実装しているコードが呼ばれることになる．
`UserInputPort`は，`usecase/interactor/user.go`で実装されている．

```go:usecase/interactor/user.go
package interactor

import (
	"context"

	"github.com/arkuchy/clean-architecture-sample-sample/usecase/port"
)

type User struct {
	OutputPort port.UserOutputPort
	UserRepo   port.UserRepository
}

// NewUserInputPort はUserInputPortを取得します．
func NewUserInputPort(outputPort port.UserOutputPort, userRepository port.UserRepository) port.UserInputPort {
	return &User{
		OutputPort: outputPort,
		UserRepo:   userRepository,
	}
}

// usecase.UserInputPortを実装している
// GetUserByID は，UserRepo.GetUserByIDを呼び出し，その結果をOutputPort.Render or OutputPort.RenderErrorに渡します．
func (u *User) GetUserByID(ctx context.Context, userID string) {
	user, err := u.UserRepo.GetUserByID(ctx, userID)
	if err != nil {
		u.OutputPort.RenderError(err)
		return
	}
	u.OutputPort.Render(user)
}
```
`usecase/interactor/user.go`に定義されている`InputPort`の実装(`GetUserByID`)内では，まず`port.UserRepository.GetUserByID`が呼ばれている(5)．
その後，`err`の有無により，`port.UserOutputPort.Render`か`port.UserOutputPort.RenderError`が呼ばれている(6)．
<br>
4． `usecase/interactor`では，`entity`のドメインロジックを実行する．
今回は，単純にDBから取得したデータを出力しているのでこの部分の実装はない．
<br>
5． `usecase/interactor`では，`usecase/port/userRepository`を呼び出し，DBの永続化処理を行う(`usecase/port/userRepository`は`adapter/gateway`が実装しているので，`adapter/gatewayのMethod`が呼ばれる)

```go:adapter/gateway/user.go
package gateway

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"log"

	"github.com/arkuchy/clean-architecture-sample-sample/entity"
	"github.com/arkuchy/clean-architecture-sample-sample/usecase/port"
)

type UserRepository struct {
	conn *sql.DB
}

// NewUserRepository はUserRepositoryを返します．
func NewUserRepository(conn *sql.DB) port.UserRepository {
	return &UserRepository{
		conn: conn,
	}
}

// GetUserByID はDBからデータを取得します．
func (u *UserRepository) GetUserByID(ctx context.Context, userID string) (*entity.User, error) {
	conn := u.GetDBConn()
	row := conn.QueryRowContext(ctx, "SELECT * FROM `user` WHERE id=?", userID)
	user := entity.User{}
	err := row.Scan(&user.ID, &user.Name)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, fmt.Errorf("User Not Found. UserID = %s", userID)
		}
		log.Println(err)
		return nil, errors.New("Internal Server Error. adapter/gateway/GetUserByID")
	}
	return &user, nil
}

// GetDBConn はconnectionを取得します．
func (u *UserRepository) GetDBConn() *sql.DB {
	return u.conn
}
```
3の`usecase/interactor/user.go`で呼び出された`UserRepository.GetUserByID`は`adapter/gateway/user.go`で実装されている．
`adapter/gateway/user.go`では，DB操作を実装している．
<br>
6． `usecase/interactor`では，`usecase/port/outputPort`を呼び出し，出力を行う(`usecase/port/outputPort`は`adapter/presenter`が実装しているので，`adapter/presenter`のMethodが呼ばれる)

```go:adapter/presenter/user.go
package presenter

import (
	"fmt"
	"net/http"

	"github.com/arkuchy/clean-architecture-sample-sample/entity"
	"github.com/arkuchy/clean-architecture-sample-sample/usecase/port"
)

type User struct {
	w http.ResponseWriter
}

// NewUserOutputPort はUserOutputPortを取得します．
func NewUserOutputPort(w http.ResponseWriter) port.UserOutputPort {
	return &User{
		w: w,
	}
}

// usecase.UserOutputPortを実装している
// Render はNameを出力します．
func (u *User) Render(user *entity.User) {
	u.w.WriteHeader(http.StatusOK)
	// httpでentity.User.Nameを出力
	fmt.Fprint(u.w, user.Name)
}

// RenderError はErrorを出力します．
func (u *User) RenderError(err error) {
	u.w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprint(u.w, err)
}
```
3の`usecase/interactor/user.go`で呼び出された`UserOutputPort.Render`(`RenderError`)は`adapter/presenter/user.go`で実装されている．
`adapter/presenter/user.go`では，Headerを付与して出力を行っている．

## アダプターの差し替え
上のサンプルコードのように，技術的な実装は全てInterface AdaptersレイヤとFrameworkd & Driversレイヤで行っている．それよりも内側の層は，技術が何を使われているかを知ることがない．
したがって，HTTP出力ではなく，ファイル出力に変えたければ，`adapter/presenter/user.go`の`Render`と`RenderError`を変えるだけで良い．

# まとめ
Clean Architectureでは，アプリケーションから技術を分離することが重要である．
その観点では，Hexagonal Architecture(ヘキサゴナルアーキテクチャ)でもClean Architectureと同様に，アプリケーションから技術を分離することができる．
それらの違いはレイヤ構造の細分化の程度であり，Clean Architectureの方がHexagonal Architectureよりも細分化されている．
ただし，Hexagonal Architectureを実際に用いることを考えると，レイヤをさらに細かく分割すると思われるので，結局，Clean Architectureに類似していくと考えられる．

また，ここで紹介したパッケージ構成はあくまでも一例である．
例えば，今回は`UserRepository`を`usecase/port`内に配置したが，`UserRepository`を`entity`に置くという選択肢などもある．
([Clean Architecture で実装するときに知っておきたかったこと](https://christina04.hatenablog.com/entry/go-clean-architecture))

# 参考文献
この記事は以下の情報を参考にして執筆しました．
・[pospomeのサーバサイドアーキテクチャ（PDF版）](https://booth.pm/ja/items/1045782)
・[Clean Architecture で実装するときに知っておきたかったこと](https://christina04.hatenablog.com/entry/go-clean-architecture)
・[The Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)