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

初級 | 5分 で読める | 2026.06.14

公式ドキュメント

リポジトリパターンは、Domain Objectの保存と取得をコレクション風のinterfaceで表し、SQLやORMを隠すパターンです。

Use Caseがdomainの言葉でRepository interfaceを呼び、外側の実装だけがORMやSQLを扱ってdomain objectを返し、table単位CRUDとは区別する図

Collectionのような境界

ユースケースは「Orderテーブルを検索する」ではなく、「未発送の注文を探す」のようにDomainの言葉で依頼します。

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

実装側でORMやSQLを扱います。

class SqlOrderRepository implements OrderRepository {
  constructor(private readonly db: Database) {}

  async findById(id: string): Promise<Order | null> {
    const row = await this.db.orders.findUnique({ where: { id } });
    return row ? toDomainOrder(row) : null;
  }
}

ORM EntityやQuery Builderを外へ返すと、利用側が永続化技術へ依存します。

ORMとの違い

ORMは、objectとrelational databaseの変換、query生成、変更追跡などを行う技術です。Repositoryは、アプリケーションから永続化をどう見せるかという設計上の境界です。

ORM APIで十分な小規模CRUDなら、同じ操作を包むだけのRepositoryは不要です。

一方、次のような場合は専用Repositoryが役立ちます。

  • 複数テーブルから1つのAggregateを復元する
  • Domain ModelとDB Schemaの形が異なる
  • テストで永続化実装を置き換えたい

境界に置かないもの

業務ルール

割引や注文確定の可否はDomainやUse Caseが判断します。Repositoryは保存・取得に集中します。

無制限な汎用CRUD

findAllupdateAnyを機械的に作らず、操作をDomainの言葉で定義します。

Transactionの調整

1つのRepositoryが個別保存を担当するのに対し、複数Repositoryの変更を同じtransactionへまとめる役割はUnit of Workです。

まとめ

  • RepositoryはDomain Objectの集合のような保存・取得境界
  • interfaceはSQLではなくDomainの言葉で表す
  • ORMは実装技術であり、Repositoryとは役割が異なる
  • 業務ルールと複数変更のtransaction管理を持ち込まない

複数変更はUnit of Workパターン、Domain Modelはドメイン駆動設計(DDD)を参照してください。

参考リソース

← 一覧に戻る
PR
PR
PR
PR