永続指向のリポジトリ
最後に「永続指向のリポジトリ」のコードについて紹介します。先ほど紹介した通り、永続指向は対象ストレージがNoSQLなどのキーバーリューストアの時に使用し、集約の情報全体をまとめて保存します。インターフェイス(Java/C#)は次の通りです。
// ■プロダクト集約を管理するリポジトリ public interface ProductRepository { // ▼プロダクト集約を取得(テナントID+プロダクトID) public Product productOfId(TenantId aTenantId, ProductId aProductId); // ▼リポジトリに保存 public void save(Product aProduct); // ▼リポジトリから削除 public void remove(Product aProduct); ~中略~ }
集約全体をMongoDBやLevelDBの1つのレコード(ドキュメント)として保存するため、AddメソッドではなくSaveメソッドを使います。それ以外はコレクション指向のインターフェイス設計と同じです。
永続指向のリポジトリ実装クラス
続けて、実装クラスのコード(Java)を見てみましょう。ここではNoSQLのひとつであるLevelDBによる実装となっています。
// ■LevelDBでの実装リポジトリ public class LevelDBProductRepository extends AbstractLevelDBRepository implements ProductRepository { private static final String PRIMARY = "PRODUCT#PK"; // ▼コンストラクタで、DBの場所として、コンテキスト名を使用 public LevelDBProductRepository() { super(LevelDBDatabasePath.agilePMPath()); } // ▼プロダクト集約を取得(テナントID+プロダクトID) @Override public Product productOfId(TenantId aTenantId, ProductId aProductId) { // 取得に使うキーを設定する LevelDBKey primaryKey = new LevelDBKey(PRIMARY, aTenantId.id(), aProductId.id()); Product product = LevelDBUnitOfWork.readOnly(this.database()) .readObject(primaryKey.key().getBytes(), Product.class); return product; } // ▼プロダクトをリポジトリに保存 @Override public void save(Product aProduct) { LevelDBKey lockKey = new LevelDBKey(PRIMARY, aProduct.tenantId().id()); LevelDBUnitOfWork uow = LevelDBUnitOfWork.current(); uow.lock(lockKey.key()); this.save(aProduct, uow); } ~中略~ }
ここではLevelDBに対する保存/削除/取得処理を記述しています。NoSQLでは、データの格納と取得のために一意なキーの設計が必要です。IDDD本では「境界づけられたコンテキストの短縮名」「集約の名前」「一意な識別子」を組み合わせる案を提示しています。
このプロダクトという集約の例であれば、まずDBそのものをコンテキスト名(LevelDBDatabasePath.agilePMPathの部分)にて分離し、それ以外の集約を示す定数「PRODUCT#PK」と一意な識別子「テナントID」+「プロダクトID」の組み合わせをキーとして使用しています。
以上、永続指向によるリポジトリの実装方法について紹介しました。
最後に
本稿ではDDDにおけるリポジトリについて紹介しました。集約の永続化を管理するにあたって、いくつかの設計指針、実装パターンがあることが理解できたと思います。次回の第13回では「境界づけられたコンテキストの結合」について紹介します。