私はレガシー倉庫システムに取り組んでいます。対応するProduct
を持つProductRepository
の集約ルートが1つあります。
現在、私は次のことを言う新しい要件を持っています:
Some Products are Purchasable and need to keep track of the datetime they have become purchasable.
したがって、この要件を実装するために、「is-a」関係がわかるため、PurchasableProduct
を継承するProduct
という新しいクラスを作成し、この新しい属性を追加するというアプローチをとることにしました。
class PurchasableProduct(Product):
def __init__(product_properties, purchasable_datetime):
super().__init__(product_properties)
self.purchasable_datetime = purchasable_datetime
今私を悩ませているのはリポジトリです。もちろん、ProductRepository
は、Products
のインスタンスを返す必要があります(PurchasableProducts
である場合でも)。ただし、これらのPurchasableProductsを取得して保存する方法が必要です。 PurchasableProductsRepository
を追加することは解決策のようですが、ProductRepository
とPurchasableRepository
の2つのレポジトリを作成できるので、PurchasableProducts
のインスタンスを保存するために使用できます。
DDDパラダイムでは、集約ルートが別のルートの特殊化であるというこの状況を実装する最良の方法は何でしょうか?
あなたの直感は正しいです。ProductとPurchasableProductの両方をProductRepositoryから取得する必要があります。
これは問題ではありません。一方が他方から継承するため、PurchasableProduct
フィールドを追加してデータベースに追加のテーブルを追加し、Product
selectステートメントに左結合してから、新しいProduct
をインスタンス化して入力する代わりにnullでない場合は、新しいPurchasableProduct
をインスタンス化します。それでも製品の配列/リストを返すことができます。たとえば、オーバーライドされたPurchase()メソッドを呼び出すと、PurchasableProduct
はその日付をチェックし、Product
はチェックしません。
基本的に、クラスのサブタイピングは、Purchase
メソッドの条件ステートメントとProductの一部のnull許容フィールドを置き換えるだけです。そのすっきりしたコードですが、アプリケーションの全体的なフローを変更するべきではありません。
購入可能な製品のみを取得する必要がある場合は、GetProductsWhichArePurchasable()
メソッドをリポジトリに追加して最適化できるようにすることができます。それでもProduct
sまたはPurchasableProduct
sのリストを返すかどうかは興味深いです。純粋主義者は製品にこだわると思いますが、とにかくあなたが最適化しているので......
2つのリポジトリを持つことができるのは奇妙なことです
それに慣れる。
ユースケースを明示的に をモデル化することは、長い間優れたプラクティスと見なされてきました。アプリケーションの観点からは、集約ルートの特定のフレーバーへの参照を提供する役割を果たすリポジトリへの参照があります。
だからあなたは持っているかもしれません
interface Product {
// ...
}
interface ProductRepository {
Product get(Id id);
}
interface PurchasableProduct {
// ...
}
interface PurchaseableProductRepository {
PurchasableProduct get(Id id);
}
PurchaseableProductsRepositoryにワイヤリングされたPurchaseableProductsにアクセスする必要があるユースケース。
アプリケーションは、基礎となる実装が同じかどうかを知る必要はありません。
基礎となる実装は間違いなくトリッキーになる可能性があります。たとえば、その情報を知らないリポジトリを使用して製品が更新された場合に、購入可能な日付時刻を失いたくないとします。
私がそれを考えるようになった方法:集約の永続的な表現は、ドメインモデルの過去のバージョンから現在までのmessageです。 (および将来)。したがって、メッセージの互換性の基本原則が引き続き適用されます。必要なのは must-ignore and must-forward セマンティクスです。
つまり、ProductRepositoryを使用してPurchaseableProductを格納する場合、実装が何も知らないプロパティを上書きしないようにする必要があります。