今日は DDD のリポジトリについてまとめます。
リポジトリとは
リポジトリは集約の保存、取得をおこなうコンポーネントです。
ドメインによって業務ロジックを処理する際、処理対象のデータを取得したり、処理結果を保存したりする機能をドメインから隔離することで、データベースなどのストレージとのやりとりをドメインやアプリケーションから隠ぺいします。
// ユーザーリポジトリのインタフェース
interface UserRepositoryInterface
{
public function create(User $user);
public function findById(UserId $id);
public function edit(User $user);
}
// クライアント側
class UserGetInteractor implements UserGetUseCaseInterface
{
private $repository;
public function __construct(UserRepositoryInterface $repository)
{
$this->repository = $repository;
}
public function handle(UserGetInputData $inputData)
{
UserId $id = UserId($inputData->getId());
$user = $this->repository->findById($id);
:
}
}
リポジトリにはインタフェースをつくり、アプリケーション側からはそのインタフェース経由でリポジトリにアクセスするようにしておくと、リポジトリの実装をかんたんに切り替えることができるようになります。
リポジトリの実装を切り替え可能にしておくと、たとえばテスト時にはデータベースを単なるメモリ上のデータにすることで、データベースの構築ができていない段階でもアプリケーションサイドのテストができるようになります。
リポジトリは集約に対応する
リポジトリを作る際に重要なのは、どういう単位で作るのか、ということにあるとおもいます。
テーブルごとに作ったりユースケースごとに作るのではなく、基本的には集約ごとに作ります。
なぜ集約ごとにつくるのかというと、これはおそらく**「データの整合性がとれなくなる可能性を設計段階で摘み取っておく」**ということが重要だからだとおもいます。
集約というのは、ひとつ以上のオブジェクトの集合であり、トランザクションの単位となるものです。
集約にはひとつのルートが存在し、配下のオブジェクトは他の集約からは隠ぺいされ、集約を介してしかアクセスできないようにします。
集約内部のオブジェクトはルートが責任をもって管理するということになります。
このように、オブジェクトへのアクセスにある程度の制約をもうけることで、無秩序にインスタンスが生成・変更され、ソフトウェアが実現したい業務のなかではありえないデータ生成や操作が発生する可能性を減らします。
リポジトリが細切れだと...
リポジトリが集約に対応しておらず、ひとつの集約の中に対応するリポジトリが複数存在するとどういう問題が発生するでしょうか。
考えられるのは、トランザクションが一貫性を保つように、注意してリポジトリを利用しなくてはならなくなることです。
集約というのは、不変条件(一貫性ルール)を強制させる範囲であり、トランザクションが完了するたびに、その集約がもつ不変条件は真である必要があります。
しかし、集約内のオブジェクトを保存するために利用するリポジトリが複数にまたがっていると、リポジトリの呼び出し方によっては不変条件に違反する可能性が発生しえます。
そのため利用するときには、それら複数のリポジトリを "正しい順序で" 呼び出したり、条件によって呼び出し方を変えるなど、注意をはらって利用する必要がでてくるとおもいます。
そのような問題を未然に防ぎ、不変条件を守るための制約として、集約に対応するリポジトリを作成する、という原則になっているのだとおもいます。