【Day 11】ブログ一覧を取得し、表示する - クリーンアーキテクチャ設計

## はじめに

![スライド12.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/85b606d7-da7d-01a1-72f4-a50d3f51ab63.png)

---

**2023年アドベントカレンダー11日目**です。

https://qiita.com/advent-calendar/2023/life-sync-app

前回から「ユーザーはトップページでブログの一覧を見ることができる」を進めています。
今回はその続きからです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/272b7c0a-ca22-3db9-acc8-f52a2dd4a280.png)

今回から「ユーザーはトップページでブログの一覧を見ることができる」を進めていきます。

引き続き**Web**の開発を進めます。

## アーキテクチャ

### アーキテクチャ図

今回のブログ記事を取得し、表示するアーキテクチャを**クリーンアーキテクチャ**に則って考えてみます。

:::note info
**クリーンアーキテクチャとは**
ロバート・Ｃ・マーティン（通称**ボブおじさん**）によって提唱されたソフトウェア設計の一つです。
システムのメンテナンス性、柔軟性が高く保たれるため、多くのシステムで採用されています。
:::

https://zenn.dev/sre_holdings/articles/a57f088e9ca07d


:::note warn
まだまだ私自身理解不足な部分があると思いますが、ご容赦ください。
:::

#### **完成**

```plantuml
@startuml

package "Domains" <<Frame>> #FFFFAA {
  class BlogTitle {
    -value: string
  }
  class BlogAuthor {
    -value: string
  }
  class BlogBody {
    -value: string
  }
  class BlogDate {
    -value: Date
  }
  class Blog {
    -title: BlogTitle
    -author: BlogAuthor
    -body: BlogBody
    -createdAt: BlogDate
  }
  BlogTitle <-- Blog
  BlogAuthor <-- Blog
  BlogBody <-- Blog
  BlogDate <-- Blog
}

package "Usecases" <<Frame>> #FFAAAA {
  class BlogUsecase {
    load(): Promise<Blog[]>
  }

  package Port {
    interface BlogInputPort {
      +get(): Promise<Blog[]>
    }
    interface BlogOutputPort {
      +store(blogs: Blog[]): void
    }
    BlogUsecase ..> BlogInputPort : <<uses>>
    BlogUsecase ..> BlogOutputPort : <<uses>>
  }
  Blog <-- BlogUsecase
}

package "Gateways" <<Frame>> #AAFFAA {
  class BlogsGateway {
    blogsDriver: BlogsDriver
    +get(): Promise<Blog[]>
  }
  BlogInputPort <|.. BlogsGateway : <<implement>>
}

package "Presenter" <<Frame>> #AAFFAA {
  class BlogsPresenter {
    +store(blogs: Blog[]): void
  }
  BlogOutputPort <|.. BlogsPresenter : <<implement>>
}

package "Drivers" <<Frame>> #AAAAFF {
  class BlogsDriver {
    +get(): Promise<BlogJson>
  }
  BlogsGateway ..> BlogsDriver : <<uses>>
}


package "Web" <<Frame>> #AAAAFF {
  package "State" {
    class BlogsState {
      title: string
      author: string
      body: string
      createdAt: string
    }
    BlogsPresenter ..> BlogsState
  }
  package "Component" {
    class BlogsComponent {}
  }

  BlogUsecase <-- BlogsComponent
  BlogsState <-- BlogsComponent
}

package "BFF" <<Frame>> {
}
BlogsDriver --> BFF : <<uses>>

@enduml
```

青の`Driver`と`Web`に関しては単体テストは書かずに、E2Eテストで担保することにします。

前回E2Eテストは実装済みなので、`WEB`から作っていきます。

### 解説

#### Domain

```plantuml
package "Domains" <<Frame>> #FFFFAA {
  class BlogTitle {
    -value: string
  }
  class BlogAuthor {
    -value: string
  }
  class BlogBody {
    -value: string
  }
  class BlogDate {
    -value: Date
  }
  class Blog {
    -title: BlogTitle
    -author: BlogAuthor
    -body: BlogBody
    -createdAt: BlogDate
  }
  BlogTitle <-- Blog
  BlogAuthor <-- Blog
  BlogBody <-- Blog
  BlogDate <-- Blog
}
```

> From ChatGPT
> これはシステムのビジネスルールを反映する層です。ドメインエンティティは、ビジネスオブジェクト（例：顧客、注文）とそのビジネスロジックを含みます。この層はシステムの他の部分から独立しており、アプリケーションのコア機能を表します。

* `Blog`をドメインとしました。
* title, author, body, createdAtをそれぞれバリューオブジェクトとして定義します。
* バリューオブジェクトに関してはこちらの記事がわかりやすかったです

https://qiita.com/little_hand_s/items/f8c0d0858c895afed7e2

#### Web

```plantuml
package "Web" <<Frame>> #AAAAFF {
  package "State" {
    class BlogsState {
      title: string
      author: string
      body: string
      createdAt: string
    }
    BlogsPresenter ..> BlogsState
  }
  package "Component" {
    class BlogsComponent {}
  }

  BlogUsecase <-- BlogsComponent
  BlogsState <-- BlogsComponent
}
```

> From ChatGPT
> クライアントとサーバー間のインタラクションを処理します。これには、HTTPリクエストの処理、セッション管理、クライアントへのレスポンスの生成などが含まれます。この層はシステムの最も外側に位置し、外部のユーザーとシステム間のインタフェースを提供します。

* Reactの関数コンポーネント・Stateをこれと定義しました
* Stateの管理には`Recoil`を採用しました

https://recoiljs.org/

#### Usecase

```plantuml
package "Usecases" <<Frame>> #FFAAAA {
  class BlogUsecase {
    load(): Promise<Blog[]>
  }

  package Port {
    interface BlogInputPort {
      +get(): Promise<Blog[]>
    }
    interface BlogOutputPort {
      +store(blogs: Blog[]): void
    }
    BlogUsecase ..> BlogInputPort : <<uses>>
    BlogUsecase ..> BlogOutputPort : <<uses>>
  }
  Blog <-- BlogUsecase
}
```

> From ChatGPT
> システムが提供する具体的なビジネスケースを表します。ここでは、アプリケーションがどのようにユーザーのリクエストを処理し、どのようにドメインエンティティと対話するかを定義します。ユースケース層は、ビジネスロジックとアプリケーションロジックの橋渡しを行います。

* `ビジネスロジックとアプリケーションロジックの橋渡しを行います。`
    * これを満たすために、InputPortから受け取ったブログデータを、OutputPortに渡すという実装にします。

#### Gateway

```plantuml
package "Gateways" <<Frame>> #AAFFAA {
  class BlogsGateway {
    blogsDriver: BlogsDriver
    +get(): Promise<Blog[]>
  }
  BlogInputPort <|.. BlogsGateway : <<implement>>
}
```

> From ChatGPT
> データベースや外部APIなど、システムの外部とのインターフェースを提供します。これにより、ユースケース層は外部リソースへのアクセス方法を意識することなく、ビジネスロジックに集中できます。

* ドライバーから、ブログのデータを取得し、ドメインオブジェクトに変更してユースケースに渡すようにします

#### Presenter

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/cfe1a397-d75c-66ad-7bf1-cbb1aebd756b.png)

> From ChatGPT
> ユースケースからのデータを受け取り、それをユーザーインターフェース層（UI）で使用できる形式に変換します。これにより、UI層はユースケース層の実装詳細から切り離され、より柔軟になります。

* ユースケースからドメインオブジェクトを受け取り、Stateに格納します

#### Driver

```plantuml
package "Drivers" <<Frame>> #AAAAFF {
  class BlogsDriver {
    +get(): Promise<BlogJson>
  }
  BlogsGateway ..> BlogsDriver : <<uses>>
}
```

> From ChatGPT
> システムの入力を管理する層です。例えば、ウェブアプリケーションにおけるHTTPリクエストの処理がこれに該当します。ドライバーはユーザーからの入力を受け取り、それをシステム内の適切なユースケースに渡します。

* Bffへのリクエスト処理をここに設定します。
* BffはGraphQLサーバーなので、`Apollo Client`を使い、リクエストを送ります。

https://www.apollographql.com/docs/react/

次回からは、この構成で**テスト & 実装**を進めていきます。