ここまでの内容を踏まえつつ、代表的なパターンを紹介します。
レイヤードアーキテクチャ
アプリケーションを特定の責務を持った層(レイヤ)に分割するアーキテクチャです。
明確な定義があるわけではありませんが、以下のようなルールのもとに使われます。
- レイヤには順序がある
- 呼び出しは必ず単方向(上から下)にしか行わない
- 各レイヤは独立した責務を持ち、詳細実装を他のレイヤに漏らしてはならない
- ex) DBを取り扱わないレイヤからは、DBを直接操作できるような呼び出しができてはならない
- レイヤ間で渡すデータも、特定の実装に依存してはならない
- ex) ORMのモデルを他のレイヤに渡してはならず、別のクラスやオブジェクトに詰め直して渡す必要がある
代表的なレイヤ分割
どのような・いくつのレイヤに分けるのかは定義されていないので、自分で決める必要があります。
よく使われるパターンを以下に紹介します。
2層 (Presentation + Infrastructure)
最も基本的な分割です。
役割
- Presentation層
- 入力データから必要なデータを抽出・バリデーション
- 出力データの変換(HTML・JSONなど)
- Infrastructure層
- DBなどでのデータの保存・取得
- 他サービスにHTTPでアクセスするようなものを含む
- DBなどでのデータの保存・取得
向くユースケース
- 入力データをそのまま保存するなど、単純な入出力のみのAPI
3層 (Presentation + Application + Infrastructure)
単純なデータの入出力だけで終わらない場合に使います。
役割
- Application層
- 一連の処理の流れを記述する
- ex) サービスから指定ユーザを完全に削除するAPI
- Repository Aでユーザ関連データを全削除
- Repository Bでユーザ関連データを全削除
- ……
- UserRepositoryで指定ユーザを削除
向くユースケース
- 入力されたデータに対して何らかの加工・変換が必要な場合
- 1回の入力で複数種類のデータを扱う必要がある場合
4層 (Presentation + Application + Domain + Infrastructure)
アプリケーションで共有するビジネスロジックがある場合に使います。
役割
- Domain層
- 複数の処理で共通して使うビジネスロジックを書く
- ex) サービスの契約条件
- n歳以上 & クレカ登録済 & サービスAに契約済 & ……
向くユースケース
- 複雑、かつ複数の処理で使うロジックが存在する場合
フロントエンド(モバイルアプリなどを含む)ではDomain・Infrastructure層が薄く、代わりにPresentation層の中身が複雑化します(MVP、MVVM、Flux…etc)。
(発展) クリーンアーキテクチャ
レイヤードアーキテクチャをより発展させたものとしてクリーンアーキテクチャがあります。
レイヤードアーキテクチャは呼び出しの方向を制約するものでしたが、クリーンアーキテクチャは依存の方向を制約します。
レイヤードアーキテクチャ to クリーンアーキテクチャ
クリーンアーキテクチャはレイヤードアーキテクチャの変形として表せます。
4層のレイヤードアーキテクチャを一部並べ替えたものが以下です。
ここで各層の間にインターフェースを持たせます。
このとき、インターフェースをApplication層に持たせることにします。
ここでレイヤ間での矢印の向き(=依存の向き)を見ると、すべてDomainに向かう方向になっています。
依存の方向に従って並び替えてみましょう。ついでにPresentationとInfrastructureを統合します。
Domainを最上位とした形に整理されました。これがクリーンアーキテクチャの中核となる考え方です。
各層の役割はレイヤードアーキテクチャと同じですが、各層の関わりが異なります。
- Domain層
- そのシステムのドメイン(業務内容)を表すロジックの集合体であり、中核
- 特定のDBやフレームワーク、ライブラリに依存しない
- Application層
- Domainを利用して何かしらの操作を行う
- どう呼び出して欲しいか(Usecaseのインターフェース)、どういうデータ取得・保存処理が必要か(Repositoryのインターフェース)を公開する
- Infrastructure層
- Application層が必要とするものを用意する
なお書籍上では円形で表現されますが、考え方としては同一です。
クリーンアーキテクチャの考え方
上記のように整理したのは、以下のような考え方に基づいています。
ビジネスロジックこそが最も重要である
Domain、つまりビジネスロジックこそがシステムの中核であり、一番重要であるとみなします。
変化しやすいものを切り離す
ビジネスロジックはビジネス要件が変わらない限り変化しません。
一方で具体的な実装は性能要件やライブラリのEOLなど、システム都合で変化しやすいものです。
このため、変化しやすいものを下側(外側)に切り離すことでビジネスロジックを守ります。
具体実装はビジネスロジックに対するプラグインとして機能します。
ビジネスロジックこそが重要であり、実装は重要ではない、というのが肝です。
ビジネスロジックを守り、詳細実装はいつでも入れ替えられるようにせよ、という考え方になります。
なのでビジネスロジックの比重が高いアプリケーションに有効なアーキテクチャになります。
(発展その二)ヘキサゴナルアーキテクチャ
ヘキサゴナルアーキテクチャは、クリーンアーキテクチャと同じくビジネスロジックを外部の詳細実装から守るための考え方です。
中心にDomain/Applicationなどの中核ロジックを置き、外側にDB・Web API・画面・バッチなどの入出力を配置します。
特徴は、外部との接続部分をPortとAdapterとして整理する点です。
- Port
- 中核ロジックが外部とやり取りするためのインターフェース
- ex)
UserRepository,PaymentGateway
- Adapter
- Portを実際の技術に接続する実装
- ex) PostgreSQL用Repository、外部決済APIクライアント
つまり、アプリケーションの中核から見ると「DB」や「HTTP API」そのものではなく、必要な操作を持つインターフェースだけが見えます。
そのため、DBを差し替えたり、外部APIをモックに置き換えたりしやすくなります。
おまけ: フルスタックフレームワークとの対立
クリーンアーキテクチャ、ヘキサゴナルアーキテクチャは上記のように「実装はいつでも入れ替えられるようにするべき」ということがベースの考え方としてあります。
これはフレームワークについても同じです。「フレームワークを使いはしても結婚はするな」などと言われたりします。
これは「全部フレームワーク側で用意するから従ってもらう」という、DjangoやRuby on Railsのようなフルスタックフレームワークとは反する考え方です。
部分的に取り入れるような試みもありますが、基本的にはどこかで無理が出るので注意してください。