はじめに
Webアプリケーション設計でよく登場する Repository パターン。
「DbContextのラッパーでは?」
「CRUDしかなくて価値が薄い」
「正直なくても困らない」
そう感じたことがある人も多いのではないでしょうか。
私自身も以前は、
- Repository は DbContext のラッパー
- 正直なくても困らない存在
だと思っていました。
しかし、設計を深めていく中で、Repository の本質的な価値は
「DBを隠すこと」 ではないと気づきました。
この記事では、Repository が
- 何を抽象化する存在なのか
- 何を気にしなくしてくれるのか
- 逆に、何をしてはいけないのか
を整理していきます。
Repositoryとは何か
※ここで言う Repository は、
「とりあえず CRUD を並べたクラス」 ではありません。
一言で言うと、Repositoryとは
永続化されたデータを、あたかもメモリ上のコレクションのように扱うための境界
です。
ここで重要なのは、
- DBかどうか
- SQLかどうか
- ORMを使っているか
といった技術要素そのものではないという点です。
Repository の目的は、
「データがどこに、どうやって存在しているか」
を業務ロジックから切り離すことにあります。
Repositoryが気にしなくしてくれること
① データの取得手段(How)
Repository を使わない場合、業務コードはしばしば次のようになります。
var user = dbContext.Users
.Where(x => x.Id == id && !x.IsDeleted)
.Select(x => new User { ... })
.FirstOrDefault();
このコードは、業務ロジックが
- DB
- 削除フラグの存在
- クエリ構造
- データ取得方法
といった永続化の都合をすべて知っている状態です。
Repository を挟むと、次のようになります。
var user = userRepository.FindById(id);
業務側は
- DBかどうか
- SQLかどうか
- キャッシュがあるか
を一切考えません。
👉 「どうやって取るか」を考えなくてよくなる。
② データの所在(Where)
Repository が抽象化するのは、DBだけではありません。
- RDB
- キャッシュ(Redisなど)
- ファイル
- 外部API
といった データの所在そのもの です。
IUserRepository
{
User FindById(UserId id);
}
このインターフェースの背後で、
- キャッシュを優先する
- API → DB の順でフォールバックする
- 障害時はファイルを使う
といった実装が行われていても、 呼び出し側は一切知る必要がありません。
👉 「データがどこにあるか」を忘れられる。
③ 技術的な制約
Repository を使わないと、業務コードは次のようなことを意識しがちです。
- IQueryable / IEnumerable
- 遅延実行
- Include漏れ
- トランザクション境界
Repository を境界にすることで、 これらの技術的制約を業務から隔離できます。
Repositoryがやってはいけないこと
Repository は万能ではありません。
❌ やってはいけないことは次の通りです。
- 業務判断を持つ
- ユースケースの流れを制御する
- 「AならBする」といった分岐を書く
Repository の責務はあくまで
- データの取得
- データの保存
- 永続化に関する都合の吸収
に限定されるべきです。
よくある誤解
Repository = DBアクセスクラス?
半分正解で、半分不正解です。
Repository は DBアクセスを含みますが、 本質は
業務から永続化の存在を消すこと
にあります。
単なる DbContext のラッパーに留まっている場合、
Repository の価値はほとんど発揮されません。
// 価値が出ていない例
User Find(int id) => _db.Users.Find(id);
この場合、
- 永続化の都合を何も隠していない
- 呼び出し側の理解が何も楽になっていない
ため、Repository を置く意味はほとんどありません。
Repositoryの価値が出る瞬間
次のような兆候が出てきたら、Repository が効き始めます。
- 業務コードに Where / Include が溢れてきた
- DB構造変更が業務ロジックに波及する
- テストで DB を意識したくない
「この取得って、業務的に何?」と考え始めた
👉 「データ」より「意味」を扱いたくなった瞬間です。
CQRSとの関係
Repository が扱うのは、
業務的に意味のある集約の取得です。
一方で、画面表示のための取得は、
- 複数テーブルのJOIN
- 集計
- DTO専用構造
になることが多く、
これを無理に Repository に押し込むと、
Repository が「何でも屋」になります。
そのため CQRS では、
-
Command / Domain側
- Repository
- Entity / Aggregate
- 業務ルール・判断
-
Query側
- 表示専用の取得
- ReadModel / DTO
- 意味判断はしない
と役割を分けます。
Query は「検索」ですが、
業務的な意味を判断するための検索ではなく、
表示・参照のための取得に寄せることで、
- Repository の責務が明確になる
- Service地獄になりにくい
という効果があります。
まとめ
- Repository の価値は 「DBを隠すこと」ではない
- Repository は データの所在と取得手段を忘れさせる境界
- 無理に作るものではなく、 必要になったときに初めて効く設計要素
Repository を疑問に思った経験がある人ほど、
本質を理解したときに強力な武器になります。
設計に迷ったときは、
「業務コードから、今何を消したいのか?」
「Repositoryに迷ったら、
それが業務判断に使われる取得か、表示のための取得かを考える」
を考えてみると、
Repository を置くべきかどうかが見えてくるはずです。