フレームワークへの依存をいかに減らすか
(2021-01-13 追記) 「フレームワークへの依存度を下げるFLUDパターン」 を書きました。
この記事は CodeIgniter Advent Calendar 2020 - Qiita の25日目です。
CodeIgniter Advent Calendar 2020 の記事ですが、今日は「フレームワークへの依存をいかに減らすか」というテーマです。
フレームワークの入門書、入門記事、公式ドキュメントには多分出てこない話題です。
フレームワークの罠
CodeIgniter4 についてチュートリアルから始めて、いろいろと書いてきました。
普通にユーザガイドを読んでアプリを開発していこうとすると、アプリは以下のように階層化されます。 黒い矢印は依存です。
MVC パターン
(2020-12-30 追記) 階層の順序を修正しました。
依存とはそれを使うということです。
コントローラがモデル、ビュー、そしてフレームワークに依存します。モデルもビューもフレームワークに依存します。
要するにフレームワークのユーザーが書くコードはほぼ全てフレームワークに依存します。フレームワークと密結合します。フレームワークがないとほとんど全く動きません。
普通にフレームワークを使おうとすると、自分の書いたコードが強くフレームワークに依存するようになっています。
フレームワークへの依存の問題点
フレームワークへの依存の何が問題でしょうか?
それは、フレームワークを変更しづらいということです。これは、別のフレームワークに移行するというような大きな話だけでなく、フレームワークをバージョンアップするのも大変になるということも含まれます。
フレームワークはそれほど安定していません。バージョンはどんどん上がりますし、互換性のない変更がされるかも知れません。
あるいは、開発が止まってしまうかも知れません。今は人気があるフレームワークも 5年、10年後は誰にもわかりません。
開発が止まっても自分でメンテしていけば使えないことはないですが、メンテできるほどの技術力があるのかリソースがあるのかという問題もあります。
アプリの寿命が長ければ長いほど、フレームワークへの依存は問題になります。
そこでフレームワークへの依存をいかに減らすかということが考えられてきました。
例えば、「クリーンアーキテクチャ」には、以下の特性が書いてありますね。
- フレームワーク非依存
- テスト可能
- UI 非依存
- データベース非依存
- 外部エージェント非依存
ADOP パターン
フレームワークへの依存を減らす方法の 1つに ADOP(Application Domain Others Pattern) があります。
ADOP は以下の 3階層のパターンです。
黒い矢印は依存です。白い矢印は汎化、つまり抽象化です。
ADOP パターン
階層の説明
ドメイン層(Domain Layer)
- ドメイン駆動設計(DDD)でのドメインオブジェクトを配置する階層です。
- ドメインオブジェクトとは、ビジネスルールをカプセル化したオブジェクトです。
- ファクトリやリポジトリのインターフェースなども、通常はこの階層に配置します。
アプリケーション層(Application Layer)
- ドメインオブジェクトを使いユースケースを実現するためのオブジェクトを配置する階層です。
アザーズ層(Others Layer)
- Domain にも Application にも含まれない残り全てを配置する階層です。
- アザーズ層はすべてを一箇所で管理するのではなく、技術スタックによって分割管理します。
MVC フレームワークはどこへ?
ADOP では以下のように説明されています。
MVC フレームワークといったユーザーインターフェースを完全に内包します。
つまり、図にするとこういうことです。
ADOP パターン
今まで MVC という世界で生きてきた我々からすると衝撃的ですね。世界には MVC 以外にももっと大切なことがあったのです!
ADOP では、既存の MVC コードから Domain と Application 層のコードを分離していることがわかります。
これらのコードをフレームワークや特定の技術スタックから分離し、フレームワークや特定の技術スタックの変更の影響をあまり受けないようにします。
ADOP のルール
ADOP のルールは以下の 2つだけです。
- オブジェクトが 3つの階層のどこに分類されるかを考え、それぞれのディレクトリに配置すること
- 下の階層のオブジェクトが上の階層のオブジェクトを取り扱うときには抽象型(インターフェース)に依存すること
CodeIgniter4 でのディレクトリ構成
CodeIgniter4 でのディレクトリ構成例は、以下のようになります。
https://github.com/nrslib/scrum-app-sample-php そのままなのでなんですが。
project/
├── app/
├── builds*
├── composer.json
├── composer.lock
├── packages/ ... packages ディレクトリを追加
│ ├── auth/ ... 認証パッケージ
│ │ ├── Application/ ... アプリケーション層
│ │ ├── DebugInfrastructure/ ... アザーズ層(デバッグ用インフラ)
│ │ └── Domain/ ... ドメイン層
│ ├── basic/ ... 共通ライブラリ
│ │ ├── DebugSupport/
│ │ ├── DomainSupport/
│ │ ├── Exception/
│ │ └── Transaction/
│ └── scrum/ ... スクラムパッケージ
│ ├── Application/ ... アプリケーション層
│ ├── DebugInfrastructure/ ... アザーズ層(デバッグ用インフラ)
│ ├── Domain/ ... ドメイン層
│ └── EloquentInfrastructure/ ... アザーズ層(Eloquent用インフラ)
├── phpunit.xml
├── public/
├── spark*
├── tests/
├── vendor/
└── writable/
何が Domain と Application なのか?
どの階層に属するかの分類方法も ADOP で解説されています。
Domain 層
- ドメインの事物を直接的に表現するコード、もしくは表現を支援するコードか?
ドメインは冒頭でも解説しようとしたとおり、ソフトウェアを適用する対象領域のことです。 たとえば物流システムであれば、ドメインの事物には運送手段(トラックなど)や拠点、配送など様々なモノやコトがあります。 それらを表現するコードであれば、質問への回答は Yes になり、ドメインレイヤーに所属させます。 また、それらのオブジェクトを構築するためのファクトリ、永続化の指針を示すリポジトリのインターフェースも、表現を支援するコードであるため、この質問に関しては Yes になります。
Application 層
- ドメインの事物を表現するコードを取り扱い、ユースケースを達成するコードか?
ドメインの事物を表現するコードとはドメインレイヤーのオブジェクトのことを指し、つまり、運送手段(トラックなど)や拠点、配送など様々なモノやコトを表現したコードやファクトリ、リポジトリといった表現の支援を行うオブジェクトのことです。 それらを取り扱い、ソフトウェアのユースケースを達成するコードであれば、この質問に対する回答は Yes になります。
Others 層
Domain でも Application でもないものは、自動的に Others 層です。
まとめ
- フレームワークは変化が激しく、強く依存すると長期のサービス提供に悪影響があります。
- フレームワークへの依存を減らす方法の 1つとして ADOP があります。
- ADOP はルールも少なく比較的わかりやすいため、適用が比較的容易です。
- CodeIgniter4(や CodeIgniter3)でも ADOP を適用して、フレームワークへの依存度を下げることが可能です。
最後に
これで CodeIgniter Advent Calendar 2020 は終わりです。
CodeIgniter Advent Calendar は今まで 6回開催されましたが、25日全部埋まったのは今回 2020 が初めてです!
今年は CodeIgniter4 がついにリリースされました!
lonnieezell, jim-parry, samsonasik, MGatner そして全ての貢献者のみなさん、ありがとう!
この記事は CodeIgniter Advent Calendar 2020 - Qiita の25日目です。
参考
Date: 2020/12/25