DDD でのデータ アクセス?
-
09-06-2019 - |
質問
Evan と Nilsson の本を読んでも、ドメイン駆動プロジェクトでデータ アクセスを管理する方法がまだわかりません。CRUD メソッドはリポジトリの一部である必要があります。OrderRepository.GetOrdersByCustomer(customer) またはエンティティの一部である必要があります。Customer.GetOrders()。後者のアプローチはよりオブジェクト指向のように見えますが、単一のエンティティ タイプのデータ アクセスを複数のオブジェクトに分散します。Customer.GetOrders()、Invoice.GetOrders()、paymentBatch.GetOrders() など。挿入と更新についてはどうですか?
解決
CRUD っぽいメソッドはリポジトリの一部であるべきです...みたいな。しかし、なぜ多数の CRUD メソッドがあるのかを尋ねる必要があると思います。彼らが何を 本当に する?彼らは何ですか 本当に のために?アプリケーションが使用するデータ アクセス パターンを実際に呼び出すと、リポジトリがさらに便利になり、ドメインに特定の種類の変更が発生したときにショットガン手術を行う必要がなくなると思います。
CustomerRepo.GetThoseWhoHaventPaidTheirBill()
// or
GetCustomer(new HaventPaidBillSpecification())
// is better than
foreach (var customer in GetCustomer()) {
/* logic leaking all over the floor */
}
「保存」タイプのメソッドもリポジトリの一部である必要があります。
集約ルートがある場合、これによりリポジトリの爆発やロジックの分散が防止されます。4 x # のエンティティ データ アクセス パターンはなく、集約ルートで実際に使用するものだけです。
それが私の$.02です。
他のヒント
DDD は通常、Customer.Save でヒントを与えたアクティブ レコード パターンよりもリポジトリ パターンを優先します。
Active Record モデルの欠点の 1 つは、(ほとんどの言語で) 特に侵入的なコードを除いて、単一の永続化モデルをほぼ前提としている点です。
リポジトリ インターフェイスはドメイン層で定義されますが、データがデータベースに保存されているかどうかはわかりません。リポジトリ パターンを使用すると、InMemoryRepository を作成して、ドメイン ロジックを分離してテストしたり、アプリケーションで依存関係注入を使用してサービス層に SqlRepository をインスタンス化させることができます。
多くの人にとって、テストのためだけに特別なリポジトリを持つということはばかげているように思えますが、リポジトリ モデルを使用すると、特定のアプリケーションにはデータベースが実際には必要ないことがわかるかもしれません。単純な FileRepository でうまくいく場合もあります。必要性がわかる前にデータベースに自分自身を結び付けると、制限が生じる可能性があります。データベースが必要な場合でも、InMemoryRepository に対してテストを実行する方がはるかに高速です。
ドメイン ロジックに関する知識があまりない場合は、おそらく DDD は必要ありません。ActiveRecord は、特にほとんどのデータとほんの少しのロジックがある場合に、多くの問題に非常に適しています。
ちょっと後戻りしましょう。Evans 氏は、リポジトリがエンティティだけでなく集約ルートを返すことを推奨しています。したがって、顧客が注文を含む集約ルートであると仮定すると、リポジトリから顧客を取得すると、注文も一緒に取得されます。顧客から注文への関係をナビゲートすることで注文にアクセスします。
customer.Orders;
あなたの質問に答えると、CRUD 操作は集約ルート リポジトリに存在します。
CustomerRepository.Add(customer);
CustomerRepository.Get(customerID);
CustomerRepository.Save(customer);
CustomerRepository.Delete(customer);
私はあなたが話している両方の方法でそれを実行しました。今私が好むアプローチは、ドメインクラスがドメインクラスであることだけを気にする永続的な無視(またはPONO - Plain Ole' .Net Object)メソッドです。彼らは、それらがどのように永続化されるのか、あるいは永続化されるかどうかさえも知りません。もちろん、これについては時には実用的であり、ID などを許可する必要があります (ただし、それでも私は ID を持つレイヤー スーパー タイプを使用するだけなので、デフォルト値などを保存する単一のポイントを確保できます)
その主な理由は、私が単一責任の原則に従うよう努めているからです。この原則に従うことで、コードのテストと保守がはるかに容易になることがわかりました。また、考慮すべきことは 1 つだけなので、必要なときに変更を加えるのがはるかに簡単です。
注意すべき点の 1 つは、リポジトリが影響を受ける可能性のあるメソッドの肥大化です。GetOrderbyCustomer..すべての注文を取得..GetOrders30DaysOld..などなどこの問題に対する良い解決策の 1 つは、クエリ オブジェクト パターンを調べることです。その後、リポジトリはクエリ オブジェクトを取り込んで実行するだけです。
NHibernate のようなものを検討することを強くお勧めします。これには、リポジトリを非常に便利にする多くの概念 (アイデンティティ マップ、キャッシュ、クエリ オブジェクトなど) が含まれています。
DDD の場合でも、データ アクセス クラスとルーチンをエンティティから分離します。
理由としては、
- テスト容易性の向上
- 関心事の分離とモジュール設計
- エンティティやルーチンを追加すると、長期的にはより保守しやすくなります
私は専門家ではないので、単なる私の意見です。
Nilsson の DDD&P の適用に関する厄介な点は、彼が常に「現実世界のアプリケーションではそんなことはしないでしょうが…」で始まり、その後に彼の例が続くことです。トピックに戻ります。OrderRepository.GetOrdersByCustomer(customer) が最適な方法だと思いますが、ALT.Net メーリング リスト (http://tech.groups.yahoo.com/group/altdotnet/)DDDについて。