リポジトリパターン - 保存と取得の詳細を隠す設計

2026.06.14

公式ドキュメント

リポジトリパターンとは、ドメインオブジェクトの保存や取得を、コレクションを扱うようなインターフェースで表す設計パターンです。

一言でいうと

リポジトリは、アプリケーションの中心からDBやORMの詳細を隠すための窓口です

ユースケースは「注文を保存する」「ユーザーをIDで探す」と言えればよく、SQL、ORM、外部API、キャッシュの詳細を知る必要はありません。

登場人物

名前役割
ドメインモデル保存したい業務オブジェクト
Repositoryインターフェース保存/取得の約束
Repository実装DBやORMを使った実装
ユースケースRepositoryを使って処理を進める

Repositoryは、DBテーブルを直接表すものではなく、ドメインから見た保存・取得の入口です。

基本形

interface OrderRepository {
 findById(id: string): Promise<Order | null>;
 save(order: Order): Promise<void>;
}

ユースケースはこのインターフェースだけに依存します。

class CancelOrderUseCase {
 constructor(private readonly orders: OrderRepository) {}

 async execute(orderId: string): Promise<void> {
 const order = await this.orders.findById(orderId);
 if (!order) throw new Error("order not found");

 order.cancel();
 await this.orders.save(order);
 }
}

ここでは、DBが何か、ORMが何か、SQLがどう書かれているかをユースケースは知りません。

ORMとの違い

観点ORMRepository
主な目的DB行とオブジェクトの変換保存/取得の抽象化
関心テーブル、クエリ、リレーションドメインから見た操作
置き場所インフラ層寄りインターフェースは内側、実装は外側

ORMを使っていてもRepositoryは作れます。Repository実装の中でORMを使う形です。

何がうれしいのか

  • ユースケースからDB詳細を隠せる
  • テストでインメモリ実装に差し替えやすい
  • 保存方法を変えても中心の処理が壊れにくい
  • クエリの意図をメソッド名で表現できる

たとえば findActiveUsersForBilling() のような名前にすると、SQLの詳細より先に業務上の目的が伝わります。

よくあるアンチパターン

すべてのテーブルに機械的にRepositoryを作る

DBテーブルごとに薄いCRUD Repositoryを作るだけでは、抽象化の意味が薄くなります。ドメインやユースケースから見た保存単位で考えます。

Query Builderをそのまま外へ漏らす

Repositoryの戻り値がORMのQuery Builderになると、呼び出し側がDB詳細に依存します。必要な結果を返すメソッドに閉じ込めます。

業務ルールをRepositoryに入れる

Repositoryは保存と取得が責務です。キャンセル可否、割引計算、状態遷移などはドメインやユースケースへ置きます。

向いている場面

向いている向いていない
ドメインモデルを守りたい単純なCRUDだけ
DB実装を隠したい小さなプロトタイプ
テストで差し替えたいORMの機能を直接使うだけで十分
複雑な検索意図がある画面専用の単純な一覧

参照専用の複雑な検索は、RepositoryではなくQuery ServiceやRead Modelへ分けることもあります。

まとめ

リポジトリパターンは、保存や取得の詳細をアプリケーションの中心から隠すための設計です。

RepositoryはDBテーブルのコピーではなく、ドメインから見たデータアクセスの約束です。業務ルールとDB都合を混ぜないために使います。

参考リソース

関連記事

← 一覧に戻る
PR
PR
PR
PR