Repository

  • 2018.08.02
  • DDD
Repository

概要

ドメイン駆動設計の Repository についての解説です。

DDD 関連記事リンク

◆ ValueObject
記事リンク: https://nrslib.com/valueobject/
◆ Entity
記事リンク: https://nrslib.com/entity/
◆ AggregateRoot
記事リンク: https://nrslib.com/aggregateroot/
◆ Repository(イマココ)
記事リンク: https://nrslib.com/repository/

解説

Repository は集約の永続化を担います。

永続化はデータの保存を指します。
その保存先はファイルでもデータベースでも何でも構いません。
ソフトウェア上で生成されたデータの保存とそのデータの取り出しを担当するのがリポジトリです。

データの保存と聞くと DAO などのデータアクセスライブラリを思い浮かべるかもしれませんが全く違います。
リポジトリはそのデータアクセスライブラリを使って、「集約」を保存するオブジェクトです。

それがどういうことなのかサンプルコードを見ながら解説していきます。

サンプルを交えて

まずは User というモデルを準備します。

User モデルは name というフィールドを持つ Entity です。
名前を変更することが可能です。
この User に対する Repository のインターフェース IUserRepository を定義します。
これでリポジトリの準備は完了しました。

まだリポジトリの実装がなく、データをファイルに保存するのか、データベースに保存するのか、データベースに保存するにしても Oracle なのか MySQL なのか等が全く決まっていませんが、この段階でもうビジネスロジックを記述することができます。

例えば、指定した id のユーザーの名前を変更するロジックを記述してみましょう。
このように IUserRepository というデータの永続化を担うオブジェクトを用意しておけば、それを利用してロジックを記述することができます。

このスクリプトが正しく動くかテストをするために、実際にインスタンス化して利用してみましょう。
なんということでしょう。インスタンス化をしようにも IUserRepository を要求しているコンストラクタに渡せるオブジェクトがまだ定義されていません。

そこでテストのためのリポジトリを定義します。
テスト用ですので、連想配列を用いた簡易的なものを実装しましょう。
このリポジトリを利用してスクリプトのテストをしてみます。
ロジックは正しく動いたでしょうか。

では同じロジックで今度は永続化機構をデータベースにしたときはどうなるでしょうか。
題材にしているのは C# ということで EntityFramework を使ってみましょう。
Dispose のための仕組みが少し記述されていますが、DbContext からデータの取得をしています。

更に永続化機構を変更して Entity Framework を利用せずに SQL を利用する SQLConnection を使った場合はどうなるでしょうか。
SQL を実行して結果をもとにインスタンスを再構築しています。

このように作成したリポジトリを利用してコードを記述してみます。
DbContext を使った UserRepository を使ったコードが以下です。
ChangeUserNameScript はビジネスロジックですが、リポジトリをコンストラクタで受け取るようになっていて、その型は IUserRepository です。
先ほどはテスト用の InMemoryUserRepository を引き渡していましたが、今回のように DbContext を使った UserRepository を渡すことで、ビジネスロジックに変更を加えることなくデータベースを利用することができるのです。

つまりリポジトリはビジネスロジックが特定のインフラストラクチャ(データベースなど)と依存しないようにするためのパターンなのです。

画一的な Repository について

リポジトリについて、画一的なリポジトリインターフェースを用意することがあります。
このようなイメージですね。

一般的によく記述されているパターンではあると思います。
そのうえで私見ではあるのですが、このパターンのように画一的なインターフェースを用意するよりも、集約ごとにそれぞれの interface を一つ一つ用意する方がよいと考えています。

画一的なインターフェースの問題点は検索方法が最適化できないパターンがあることです。

例えばサークルの所属ユーザー全員の情報を取得するような処理を記述してみます。
Find メソッドが所属するユーザーの数だけ呼ばれることになります。
もし userRepository の実装が SQL の select 文を発行するリポジトリだった時のオーバーヘッドは相当のものです。

逆に専用インターフェースを用意した場合はどうなるでしょうか。
処理自体はほとんど変わってないですが、リポジトリに FindIds というメソッドを用意してユーザーの ID をコレクションで渡すようにしてあります。
userRepository の実装が select 文を発行するリポジトリだった場合にも IN 句などを使って select 文一回の発行済むでしょう。

もちろん Entity Framework 等を利用していて Query 等が動的に生成される仕組みであるなら、結果のセットに対して動的に SQL が生成されるためオーバーヘッドも殆どないのでしょうが、データの永続化機構が Entity Framework から変更されたときにそのビジネスロジックが使い物にならなくなる可能性が残ります。

まとめ

リポジトリはビジネスロジックを特定のインフラストラクチャに依存させないようにするためのパターン。
実を言うと依存関係逆転の法則の対象をインフラストラクチャ層に当てはめただけのものです。
特定のインフラストラクチャ層に結びつかないようになるのでビジネスロジックのテストが行えるようになります。

結果としてデータが ファイル / データベース / 未知の保存媒体 でも、同じようにビジネスロジックを動作させることができます。

DDDカテゴリの最新記事