この記事は社内ブログを外部向けに書き換えたものです
多くの(Webフレームワークに依存しない)Webアプリは、基本的にはMVCアーキテクチャに基づいて構成されています。
このうち、M(Model)にデータベースのデータを反映する処理について、よく以下の三つの方法が取られています。それぞれについて説明していきます。
- Active-Recordを自前実装する方法
- ActiveRecordを実現するライブラリを使用する方法
- Repositoryパターンを利用する方法
Active-Recordを自前実装する方法
そもそもActive-Recordって?
Active-Recordパターンは、Enterprise Application Patternsの一種で、一つのデータベースのテーブルと一つのクラスを対応付け、またそのクラスのインスタンスを(クラスに対応する)テーブルの一つのレコードに紐付ける、というパターンです。よくRuby on RailsやLaravelなどのWebフレームワークで用いられます。参考: P of EAA: ActiveRecord
これは一種のO/Rマッパーで、オブジェクト指向のやり方で簡単にデータベース操作ができるようになります。
Active-Recordの自前実装
多くのWFと同じようなActive-Recordパターンを実現するには非常に多くの動的コードを記述する必要があり、メンテナンスコストが増えてしまいます。
これより、自前でのActive-Recordパターンの実装は個人的には推奨しません。
PHP-ActiveRecordライブラリを使用する方法
例えば、PHPでなら
このライブラリが提供する機能を用いてActive-Recordパターンを実現することもできます。
これにより、Laravelフレームワーク等と同じような操作を簡単に実現できます
Repositoryパターンを利用する方法
Repositoryパターンも同じく、Enterprise Application Patternsの一種で、任意のデータに対し、それがどこに保存されるかを一切気にすることなくアクセスすることができるようにし、保守性や拡張性を高める、というパターンです。参考: P of EAA: Repository
主に、(フレームワークに依存しにくく、ドメイン駆動設計の影響が大きい)モバイルアプリの開発等でよく用いられています。
これはActiveRecordとは正反対のアプローチで、完全にModel(ドメインロジック)からデータベースに関する処理(インフラロジック)を取り除くことになります。
以下にRepositoryパターンの実装例を記述します。
class User{
public $name;
public $age;
}
class UserRepository{
public function getAdultUsers(): array{…}
}
ModelクラスとしてUserクラスを、Userに関するデータを提供する、という役割を全て担うUserRepositoryクラスを定義します。
ここで、Userクラスは全てのフィールドを公開し、ただの構造体として振舞います。このようなクラスは、POJO(Plain Old Java Object)、あるいはPI(Persistence Ignorance)と呼ばれ、Repositoryパターンとセットでよく使われます。
もちろん、以下のようなModel内のデータを組み合わせるようなメソッドを定義することは問題ありません。ここで大事なのは、Modelクラスが他のどのオブジェクトにも依存しないようにすることです。
class User{
public $name;
public $age;
function getBirthYear(){
return Date::now()->year - $this->age;
}
}
ここで、大人のUser一覧を取得することを考えます。コード・クライアントは該当するユーザーデータがどこに保存されているか、といったようなことは一切考慮する必要はなく、ただUserRepositoryのgetAdultUsersメソッドを呼べばいいだけです。
このように、Repositoryパターンを採用すると、完全にModel(ドメインロジック)からデータベースに関する処理(インフラロジック)を取り除くことができるようになり、データ操作の方法が非常にシンプルになります。
これはクライアントモバイルアプリのように、データが外部にあるときに特に効力を発揮するパターンです。
まとめ、結局どれを使えばいいのか
RepositoryパターンはModelからデータベースに関する処理を取り除くことにより効力を発揮するパターンです。つまり、Active-RecordパターンをRepositoryパターンでラップすることはできません。また、ActiveRecordパターンもモデルクラスとデータベースが密につながっていることにより効力を発揮するので、ReposiotryパターンをActive-Recordパターンでラップすることもできません。
WFに依存しない環境だったらもしかしたらオールRepositoryパターンがベスト(モバイルアプリではよくこれが採用される)かもしれませんが、LaravelなどのWFではActive-Recordパターンの恩恵が非常に大きいので、データベース用モデル、リモート用モデル、といったようにハイブリットで使っていくのが一番だと思います。