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

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
findAllやupdateAnyを機械的に作らず、操作をDomainの言葉で定義します。
Transactionの調整
1つのRepositoryが個別保存を担当するのに対し、複数Repositoryの変更を同じtransactionへまとめる役割はUnit of Workです。
まとめ
- RepositoryはDomain Objectの集合のような保存・取得境界
- interfaceはSQLではなくDomainの言葉で表す
- ORMは実装技術であり、Repositoryとは役割が異なる
- 業務ルールと複数変更のtransaction管理を持ち込まない
複数変更はUnit of Workパターン、Domain Modelはドメイン駆動設計(DDD)を参照してください。