導入
「DBからユーザーを探す」処理が業務ロジックの中に直書きされていると、SQLとビジネスルールが混ざって読みにくくなります。Repository パターンは、データの出し入れを「コレクションのような窓口」に隠し、業務ロジックを保存先から切り離します。
図解
flowchart LR
S["業務ロジック"] -->|Add / FindById| R["IRepository<User>"]
R -.-> DB["DB / メモリ / ファイル<br/>(差し替え可能)"]
style R fill:#e1f5fe
サンプル
IUserRepository repo = new InMemoryUserRepository();
repo.Add(new User(1, "Taro"));
repo.Add(new User(2, "Jiro"));
var u = repo.FindById(1);
Console.WriteLine(u?.Name); // Taro
record User(int Id, string Name);
interface IUserRepository
{
void Add(User user);
User? FindById(int id);
}
class InMemoryUserRepository : IUserRepository
{
private readonly List<User> _users = new();
public void Add(User user) => _users.Add(user);
public User? FindById(int id) => _users.FirstOrDefault(u => u.Id == id);
}
- Repository は「データの出し入れ」を抽象化した窓口
- 業務ロジックは
IUserRepositoryにだけ依存し、保存先(DB/メモリ)を知らない - テストではメモリ実装、本番ではDB実装、と差し替えられる
やってみよう
FindById に加えて All() で全件を返すメソッドを増やし、名前順に並べて出力してみましょう。呼び出し側はSQLを一切書きません。
演習
interface IBookRepository { void Add(string title); int Count(); }
// TODO: リストに溜めて Count() で件数を返す InMemoryBookRepository を実装してください
___
var repo = new InMemoryBookRepository();
repo.Add("C#入門");
repo.Add("設計の基本");
Console.WriteLine(repo.Count()); // 2
- 期待される出力:
2
ヒント1を見る
class InMemoryBookRepository : IBookRepository { private readonly List<string> _b = new(); public void Add(string t) => _b.Add(t); public int Count() => _b.Count; }
ヒント2を見る
内部で List<string> に溜め、Count はそのリストの件数を返します
まとめ
- Repository はデータアクセスを「コレクション風の窓口」に隠す
- 業務ロジックを保存先から切り離せる
- メモリ実装とDB実装を差し替えでき、テストが容易
次回: 複数の変更をまとめて確定する Unit of Work です。