レイヤードアーキテクチャでAPI開発[入門]

※ 本記事はレイヤー分けに終始しています。モデリングを等含む設計を検討の場合は、別の記事参照することを強くお勧めします。

## 前書き
- Goやその周辺ライブラリ、認証処理などについての説明は省きます。
- 著者の理解が甘く間違っている箇所もあるかもしれません。その際は優しく教えていただけると幸いです。

なるべく初学者にもわかりやすい様に書きました。
始めは実装コード等理解しづらいと思いますが、概念的な理解の足掛かりにしていただけると嬉しいです☺️

サンプルコード: https://github.com/karamaru-alpha/layered-arch-sample

## レイヤードアーキテクチャって何？
### 概要
**ソフトウェアの関心事をレイヤー毎に分離するための設計方法の一つ**です。
他にもクリーンアーキテクチャ・オニオンアーキテクチャなどが有名ですが、どれも**責務・依存関係を明確化する**というゴールは変わりません。

### メリット
アーキテクチャを導入する主なメリットは以下の二つです。

- DBやUI・ビジネスロジックなど、**仕様変更の際にコードの修正箇所が明確・軽微**
- 各レイヤーにおいてテストする目的が明確・処理をモックしやすいことから**テストしやすい**

仕様変更に強くテストもしやすいことから、**保守運用しやすい**ということで人気なんですね👀

## レイヤー解説
※矢印は依存方向です。依存とは、関数や変数を参照することを指します。
![スクリーンショット 2020-09-12 23.08.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334600/3214000b-905f-9221-3b40-564400713aa4.png)
レイヤードアーキテクチャの基本はこの図です。
純正レイヤードアーキテクチャでは```domain->infrastructure層```という依存関係でしたが、「ビジネスの中核をなすdomainが、使用DB(infra)などに依存する・影響されるっておかしくない？」と言う事で、domainが依存の最高位に君臨しています。これを**依存関係逆転の原則(DIP)**っていうみたいです👀

```
domain層:　ビジネスルールの中核の定義を担当
infrastructure層: DB通信・DBデータ更新を担当
usecase層: アプリケーション固有のビジネスルールを担う処理を担当
interface層: ユーザーからのリクエスト受け取りや、表示に関することを担当
```
大まかにはこんな役割です。このままではわかりづらいので以下で詳しく説明します！



### domain層
ここでは「**ドメインモデル**」と「**リポジトリ**」を定義します。

#### ドメインモデル
一言でいうと**ビジネスルールの中核・対象の存在**です。
ex)「物を売り買いする」というビジネスルールでは、「ユーザー」「商品」などがドメインモデルとして定義される。

**SQLのテーブルとよく似ていますが**、そのビジネスルールで使用しないデータの場合はdomainとして扱わないという点で**異なります**。
ex) SQLには解析用にcreated_atが保存されているが、サービス上は使用しないためドメインモデルには持たせないなど。

以下、ユーザーのドメインモデルの例です。

```path/to/domain/model/user/model.go
package user

type User struct {
	ID    int32
	Name  string
}
```

#### リポジトリ
**ドメインモデルになんらかの変更をする処理を定義する所**です。

注意したいのは、ここで定義されているモノは「ドメインモデルこうやって変更するよ！」と**抽象的に宣言しているだけで、具体的な処理を記述したものではない**という事です。「**ドメイン層**は、他の仕様変更(使用DBやUI)に影響されない(依存しない)」という思想の元、**リポジトリには具体的な処理を記述せず、抽象的な宣言に止まります**。
具体的には、[Interface](https://qiita.com/rtok/items/46eadbf7b0b7a1b0eb08)を用いて抽象的なドメインの更新処理を表現しています。

ここら辺難しいですよねorz
イメージは、リポジトリという上司が「ドメインモデルに対してこんなことしたい！」と算段なく抽象的言っている感じで、DB更新などの**具体的な処理は**infra層などの部下が**リポジトリの意図(Interface)を汲み取って行います**。

以下、リポジトリの例です。ユーザーというドメインモデルに対して、「こんな引数・戻り値で名前を更新するぞ！」という抽象的な宣言がなされています。

```path/to/domain/repository/user/repository.go
package user

import "path/to/domain/model/user"

type Repository interface {
	UpdateName(record *user.User, name string) error
}
```

### infrastructure層
ここでは**実際にDBと通信し、リポジトリに記載された「抽象的なドメインモデルの更新処理」を実現します**。

リポジトリで宣言された**Interface(抽象的な更新処理)を継承**し、infra層での実際の更新処理を**メソッド化**しています。
そうすることによって、**リポジトリで定義された抽象的な更新処理がinfra層の具体的な更新処理と紐づけられています**。(go#interfaceの仕様上、整合性が取れないとエラーを吐くためです。もっと噛み砕けば、リポジトリで宣言された抽象的な更新処理が、infra層に実装されていなければエラーを吐くということです。)


ユーザーの名前変更を例にコードをみてみましょう。

- infra層に使用DBを渡す(サーバー起動等を定義するserver.goに書いています。)
- リポジトリで定義したInterface(抽象的な更新処理)をinfra層(repositoryImpl)に継承
- UpdateNameメソッドで実際のDB更新処理を記述

を行っています。(使用DBはinfra層にベタ書きでもいいのですが、sqlmockでテストしやすいためserver.goから渡しています。)

```server.go
package server

import (
	ur "path/to/infrastructure/repositoryimpl/user"
)
...
	// mysql.Conn = sqlへのコネクション。sql.Open("mysql"...
	ur.NewRepositoryImpl(mysql.Conn)
...
```


```path/to/infrastructure/repositoryimpl/user/repositoryimpl.go
package user

import (
	um "path/to/domain/model/user"
	ur "path/to/domain/repository/user"
	"database/sql"
)
type repositoryImpl struct {
	db *sql.DB
}

// NewRepositoryImpl Userリポジトリで定義したinterfaceを継承
func NewRepositoryImpl(db *sql.DB)ur.Repository {
	return &repositoryImpl{
		db,
	}
}

// UpdateName repositoryImplのメソッドとして、実際のDB更新。
func (impl repositoryImpl) UpdateName(record *um.User, newName string) error {
	stmt, err := impl.db.Prepare("UPDATE user SET name = ? WHERE id = ?")
	if err != nil {
		return err
	}
	_, err = stmt.Exec(newName, record.ID)
	return err
}

```
### usecase層
ここでは**アプリケーション固有のビジネスルール**を記述します。
ビジネスルールってなんだよ！って感じですよね笑

例として、「一定額以上の買い物をした人に対して、ある還元率でポイントを付与する」というケースで考えてみましょう。おさらいですが、実際にUserというドメインモデルの所持ポイントを更新するのは(抽象的に)リポジトリの役目ですよね？
しかし、```いくら以上の支払いでポイントを付与するか・ポイント還元率は何%か```などは、実際の更新というよりも、**更新に使うデータの選定・整形**に近いと思います。これが「**アプリケーション固有のビジネスルール**」と言われている部分です。

それらを記述するのがusecase層です。**interface層(handler)から渡されたデータを整形し、必要に応じてそのデータをリポジトリに渡します**。

コードをみてみましょう。例示が行き来して申し訳ないですが、名前更新処理です。

- 先ほど作成した実際のデータ更新処理が記載されたinfra層(userRepoImpl)をユースケースに(リポジトリを介して間接的に)継承
- interface層から渡されたnameから更新処理を目論む。**データ更新の際は、継承されたrepositoryImplを(リポジトリを介して間接的に)叩く**。

```server.go
package server

import (
	ur "path/to/infrastructure/repositoryimpl/user",
	uu "path/to/usecase/user"
)
...
	userRepoImpl := ur.NewRepositoryImpl(mysql.Conn)
	uu.NewUseCase(userRepoImpl)
...
```

```path/to/usecase/user/usecase.go
package user

import (
	um "path/to/domain/model/user"
	ur "path/to/domain/repository/user"
)

// interface層で参照するためInterfaceで切り出し
type UseCase interface {
	UpdateName(user *um.User, name string) error
}


type useCase struct {
	repository ur.Repository
}

// NewUseCase Userリポジトリ(domain層)を継承したrepositoryImpl(infra層)を間接的に参照する
func NewUseCase(userRepo ur.Repository) UseCase {
	return &useCase{
		repository: userRepo,
	}
}


// UpdateName ユーザーの名前を更新するユースケース
func (ur useCase) UpdateName(user *um.User, name string) error {
	return ur.repository.UpdateName(user, name)
}
```

### interface層
ここでは**request/responseの整形・バリデーション**を行います。

ユーザーへの表示をメインとしたこの層では、**具体的なビジネスロジックは書かない**ことが重要です。APIでは**リクエストが不正値かどうかを確認した後ユースケースを呼び出し、返り値を整形してレスポンスすることだけが役目**です。例えば名前更新処理では、長すぎる文字列クエストはこの層でrejectします。

また```interface```はGo言語の**予約語**なので、ディレクトリ作成時は```interfaces/```とかにしましょう。

以下、名前更新の例です。

- 先ほど作ったuserのusecaseを継承
- リクエストのバリデーションを行い、そのデータをusecaseに渡す(呼び出す)。
- 返り値を整形してレスポンス

```server.go
import (
    ur "path/to/infrastructure/repositoryimpl/user",
    uu "path/to/usecase/user",
    uh "path/to/interfaces/api/handler/user"
    "net/http"
    "github.com/labstack/echo"
)

func Serve(addr string) {
    userRepoImpl := ur.NewRepositoryImpl(mysql.Conn)
    userUsecase := uu.NewUseCase(userRepoImpl)
    userHandler := uh.NewHandler(userUsecase)


    e := echo.New()
    // contextにユーザー情報を入れるAuthenticateは各々でお願いします。
    e.PATCH("/user/update", Authenticate(userHandler.HandleUpdate())))

    
    ...サーバー起動処理...
}
```

```path/to/interfaces/api/handler/user.go
package user

import (
	um "path/to/domain/user"
	uu "path/to/usecase/user"
	"encoding/json"
	"http/net"
)

// Handler UserにおけるHandlerのインターフェース
type Handler interface {
	HandleUpdate(c echo.Context) error
}

type handler struct {
	useCase uu.UseCase
}

// NewHandler Userデータに関するHandlerを生成
func NewHandler(userUseCase uu.UseCase) Handler {
	return &handler{
		useCase: userUseCase,
	}

// HandleUpdate ユーザ情報更新処理
func (uh handler) HandleUpdate(c echo.Context) error {
	type response struct {
		Message string `json:"message"`
	}

	requestBody := new(um.User)
	if err := c.Bind(requestBody); err != nil || requestBody.Name == "" {
		return c.JSON(http.StatusBadRequest, &response{Message: "User updation failed"})
	}

	...名前の長さバリデーションとかもここで行う...
	...変数userにcontextなどから現在のユーザー情報を入れる処理...

	if err := uh.useCase.UpdateName(user, requestBody.Name); err != nil {
		return c.JSON(http.InternalServerError, &response{Message: "User updation failed"})
	}

	return c.JSON(http.StatusOK, &response{Message: "User successfully updated"})
}
```

## まとめ
以上がレイヤードアーキテクチャの構成例解説です。
稚拙な文章を最後まで読んでくださりありがとうございました。
ご指摘・ご感想あればコメントか、[twitter](https://twitter.com/karamarooon)にまで連絡していただけると幸いですmm


参考にした記事は以下ですmm
①[今すぐ「レイヤードアーキテクチャ+DDD」を理解しよう。（golang）](https://qiita.com/tono-maron/items/345c433b86f74d314c8d#%E6%9C%80%E5%BE%8C%E3%81%AB)
②[【Golang + レイヤードアーキテクチャ】DDD を意識して Web API を実装してみる](https://yyh-gl.github.io/tech-blog/blog/go_web_api/)

それではみなさん、楽しい開発ライフを！！