フロントエンドにクリーンアーキテクチャはいらない（ことが多いと思う）

## Disclaimer

- 個人の意見です。
- クリーンアーキテクチャが必要な場面も、もちろんあると思っています。
- クリーンアーキテクチャに精通しているわけではないので、勘違いも含まれていると思います。

## 最近考えたこと

クリーンアーキテクチャで領域をきれいに分割し、ドメイン知識を中心に据えた同心円の構造が作れると、「きれいに実装できた」感があります。

ただ、私の関わるプロジェクトのアプリ開発では、「それってオーバースペックなのでは？」と思う場面が増えてきました。
そこで、その考えをまとめようと思ったのが、この記事です。

直近では、Flutterのアプリを開発するときに考えたのでそれが中心にはなりますが、Webフロントエンドや他のスマホアプリ実装でも同じように考えられそうだと思っています。

## 私のクリーンアーキテクチャの理解

https://blog.tai2.net/the_clean_architecture.html

本当にざっくりとした理解としては、以下のとおりです。

- ハイレベルな概念を中心に据える
- 外部環境はその外側に配置する
- 依存関係は外側から内側のみとする
    - それを実現するために、依存関係逆転の原則（DIP）が利用できる

雑に図にすると、以下のようになると思っています。

```plantuml
@startuml
skinparam componentStyle rectangle

[presentation] -> [domain]
[domain] <- [data]
@enduml
```

### クリーンアーキテクチャだと何が嬉しいのか

外部環境を、概念（アプリが実現したい価値）から分離できること、だと考えています。

これによって、以下のメリットが生まれます。

#### ドメインの実装を保ったまま、フレームワークやDBの更新や差し替えができる

例えば、1つのドメイン実装で、複数のUIを作成することができます。

- 例: Kotlin Multiplatform Mobileで概念を実装しておくことで、iOSとAndroidそれぞれに適したUIを構築。
- 例: Pure JavaScriptで概念を実装しておくことで、ReactとReact NativeでUIを構築。

データ取得を抽象化しておくことで、Web API取得だったものを内部DB取得に変えたりすることができます。 [^1]

[^1]: ただこれ、リポジトリパターンのメリットな気もしてます。

- 例: Realm使っていたところから、SQLiteに置き換え。

#### ビジネスルールを、外部の影響なくテストできる

ビジネスルールのテストをするために、DBのセットアップや遅いWeb APIのレスポンスを待つ必要が無くなります。
（それらはハイレベルに抽象化されているので、スタブ化を無理なく行える）

## 私が関わっているプロジェクト

- 新規の受託開発案件が多いです。（今までとはちょっと違うビジネスを、これから始めるぞーというお客さんが多いです）
- フロントエンドからの入力はテキストや画像のアップロードぐらいで、それをバックエンドで溜め込み、フロントエンドではそれを整形して表示するような案件が多いです。（SNSや、情報の検索サービスなど）
- フロントエンドとバックエンドAPIを、同時に開発することが多いです。

そんな中では、フロントエンドは「ロジックを持って概念を表現する領域」ではなく、「バックエンドから返却された情報を見やすく表示する」ことが関心の中心となります。
（世に言う「JSON色付け係」）
フロントエンド側に表示項目を追加する場合には、バックエンド側からのAPIレスポンスに追加してもらい、フロントエンドコード内で引き回してUIまで伝搬させます。

また、BtoCの案件が多いです。
「業務プロセス」を実現するというイメージよりは、「ユーザー体験」を実現するイメージです。
発注側の多くは、「データがどのように処理されていくか」というよりは、「どんな価値をユーザーに提供（見せる）か」を主眼としています。（と感じています。）

## クリーンアーキテクチャを愚直にフロントエンドに導入しようとしてどうなったか

大量のボイラープレートコードが発生しました。

### 似たようなデータ表現

インターフェイスアダプターでは、Web APIに完全に一致したデータクラスを作成しました。
それとは別に、Use Caseが利用するEntityを作成しました。

APIレスポンスとUIがほぼ同一の構造となっているため、結果として差はほとんどありませんでした。
インターフェイスアダプター側に、相互に変換する処理を実装しましたが、同一のプロパティを移し替えているだけのものがほとんどでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8708/f8001925-a887-5fa9-fb24-3862828168a0.png)


### 過度の（と感じた）外部環境の抽象化

Web APIの呼び出しを抽象化したインターフェイスを、ユースケースの層に定義しました。
ただ、現時点ではそこが置き換わる想定はありません。
API呼び出し処理を追加したい場合に、修正箇所が2箇所に分散してしまっているだけと感じました。

## クリーンアーキテクチャをやめて、どうしたのか

単純な、3層のレイヤードアーキテクチャとしました。

```plantuml
@startuml
skinparam componentStyle rectangle

[presentation] --> [domain]
[domain] --> [data]
[presentation] --> [data]
@enduml
```

presentation層では、data層にあるWeb APIのレスポンスをマッピングしたデータ構造をそのまま利用します。
data層では単純なWeb API実行や内部データの保存などを行い、その統合はdomain層として管理することとしました。

## 逆に、クリーンアーキテクチャがフロントエンドに必要な場面

### 1. バックエンドAPIとフロントエンドが一体ではない場合

バックエンドAPIの語彙と、フロントエンドの語彙が異なっている場合や、
対象としているドメインが違っている場合。
例えば、Qiita APIを利用して、何かに特化したQiitaクライアントを作る場合などは、これに該当すると思います。

複数の外部環境を統合することが価値となるアプリ。
マッシュアップ系のアプリでは、それぞれの用語を変換して別の概念として価値提供することになるかと思います。

### 2. フロントエンド側での状態遷移や処理が複雑

状態遷移や保存に関わる判断をフロントエンド側で多く管理している場合。
バックエンドAPIを利用しないスタンドアローンなアプリなど。

### 3. 関心の外側の変更頻度が高く、相対的に概念は安定している

外部のAPIなどが、自分たちのチームとは独立して開発されており、それがアプリ内部の概念とはあまり影響しない場合。

また、UIはA/Bテストなどで頻繁に変更されるが、表示されるデータ自体は安定している場合。

## 最後に

適切なアーキテクチャを考えるのは難しいですね。
それをメンバー全員が理解して、実践し続けていくのは、もっと難しいですね。

プロジェクトの特性や、メンバーの特性に合わせて、最適なものを採用したいものです。


## 参考

- [クリーンアーキテクチャ(The Clean Architecture翻訳)](https://blog.tai2.net/the_clean_architecture.html)
- [世界一わかりやすいClean Architecture](https://www.nuits.jp/entry/easiest-clean-architecture-2019-09)