はじめに
最近のWeb API設計の流行として、従来RailsやLaravelで採用されていたMVCだけでなく、Clean Architectureなど新たなアプローチへの関心が強まっています。そうした中で、ただ紹介された設計方法を丸パクリするのではなく、自分自身である程度理解し実践する力を身に着けることが大切かなと思います。
Clean Architectureの原典においてもディレクトリ構成の例が紹介されていますが、一方で必ずしもこの例に従う必要はなく、アプリの要件に合わせて柔軟に変更すべきと著者は述べています。
そこで、本記事ではWeb APIのディレクトリ構成を決定するフローとその考え方について、自分なりの考え方を述べていこうと思います。
1. 分割する単位を決める
設計の第一段階として、各ディレクトリにどのような役割を持たせるか決定することから始めます。 例えば、MVCであればControllerとModelは複数の役割を担うことになりますし、Clean Architectureでは単一責務原則に基づき、1ディレクトリ=1責務で構成します。
以下の表はそれぞれのディレクトリ構成と役割の関係をまとめたものになります。実際にはヘルパーやサービスクラスなどが必要に応じてディレクトリが追加されます。
ちなみにControllerとUsecaseの分離には以下の長所・短所があります。
-
メリット
- 責務が分離されてるため、シンプルに読みやすい。
- ユースケースが再利用性しやすくなる。
- ユースケースを外部環境に依存せずテスト出来るようになる。
-
デメリット
- 開発コストの増加。特に小規模なアプリではオーバーエンジニアリングになりがち。
ModelとRepositoryの分離にも↑とほぼ同じ長所・短所があります。
安定度を決める
続いて各ディレクトリの安定度を決める作業をします。
安定度とはモジュールの変更のしやすさを表す指標です。 安定度を決めるとは即ち、将来的な変更に柔軟に対応出来るようにしたい場所を決める作業とも言えます。
安定度の高いモジュールは他のモジュールから多く依存されます。 安定度の高いモジュールを変更すると、依存先にも変更の影響が出る可能性があります。結果として変更がしづらく修正の機会が少なくなります。
一方、安定度の低いモジュールは他から依存されてはいけません。 被依存が少ないモジュールは、他への影響を気にせず気軽に変更が出来るようになります。
安定度を決める基準として、例えばClean Architectureでは外部環境からの独立という思想に基づいています データベースやクライアントの変更にアプリが影響されないようにするため、外部とのインターフェース層(ControllerやRepository)の安定度を低く保っています。
他方、一般的なWebアプリでは外部環境は不変という前提で構成されたものが多いです。 クライアントとの通信はHTTPを前提としていますし、DBを将来的に変更することも基本的には想定していません。その為、インターフェース層の安定度を高くし、代わりにビジネスロジックやビューを柔軟に変更出来るようにします。
各モジュールにどのくらいの安定度を持たせるかは、アプリの要件によって異なります。 数十年の稼働を見据えたアプリでは外部環境が変わる可能性が高いため、前者の方法に分があるかもしれません。しかし、一般的なWebアプリでは開発速度を優先するという意味でも、後者の方法が好ましいのではないでしょうか。
依存関係を整理する
ここで一度ディレクトリ間の依存関係について整理します。以下はClean Architectureの依存関係のフローを表したものです。(正確ではないと思うけど...)
ちなみに依存には明示的な依存と暗黙的な依存の二種類があります。
明示的な依存とは、コード上で表現されている依存関係です。 Javascriptで言えば、ファイルの先頭に書いてあるimport文がまさにそれです。
一方、暗黙的な依存とは、コード上に表現されない依存関係のことです。 言語による制約やフレームワークに依るルールなどがこれにあたります。例えば、RailsのActiveRecordはRDBを前提としており、テーブルとモデル名は同じにすることが求められます。
依存関係を逆転させる
安定度と依存関係には密接な関係があります。 先ほど述べた通り、安定度の高いモジュールは他から多く依存され、安定度の低いモジュールは他のモジュールに依存されてはいけません。
これをルール化すると、安定度の高いモジュールは安定度の低いモジュールに依存してはいけないということです。 これは一般に安定依存の原則と呼ばれています。
安定依存の原則に反している箇所では、依存関係を逆転させる必要があります。 具体的な方法はこちらに譲りますが、依存関係の逆転は双方のコンポーネントを抽象クラスに依存させることで行われます。具体的な実装を持たない抽象クラスは安定度が高いため、これに依存させることで安定依存の原則を達成させるというカラクリです。
実際にClean Architectureの依存フローを見ると、UsecaseとRepositoryの間でこの原則に反しています。そこでRepositoryを抽象化したInterface Repositoryクラスを作成し、お互いをこの抽象クラスに依存させるようにすることで、原則を順守させます。
補足すると、安定依存の原則は絶対的なものではありません。アプリの要件によって例外を許容しても勿論OKです。原則の適用にはその背後にある目的を理解し、それが現状に適しているかどうかを適切に評価することが重要です。
まとめ
改めてディレクトリ構成の決定フローをおさらいします。
1. ディレクトリの役割を決定する
2. 各ディレクトリの安定度を決める
3. 依存関係のフローを整理する
4. 安定度に基づき依存関係を逆転させる
以上の流れに沿うことで、将来的な変更が必要な部分は柔軟性を保ち、変更の無い部分は安定度を持ったアプリを設計することが出来るようなります。状況に応じたベストな設計選択をすることで、より優れたアプリ開発をじつげんしましょ。