I'm working on a legacy warehouse system. There is one Aggregate root, Product
which has its correspondent ProductRepository
.
Right now I have a new requirement that says the following:
Some Products are Purchasable and need to keep track of the datetime they have become purchasable.
So, in order to implement this requirement, I decided to take the following approach that, since I can see a "is-a" relationship, I decided to create a new class called PurchasableProduct
which inherits Product
and adds this new attribute.
class PurchasableProduct(Product):
def __init__(product_properties, purchasable_datetime):
super().__init__(product_properties)
self.purchasable_datetime = purchasable_datetime
What's bugging me right now are the repositories. ProductRepository
, of course, should still return instances of Products
(even though they might be PurchasableProducts
), but I need a way to retrieve and save those PurchasableProducts
. Adding a PurchasableProductsRepository
seems a solution but it's kind of weird that I can have two repositories, ProductRepository
and PurchasableRepository
, that I can use to save instances of PurchasableProducts
.
In a DDD paradigm, what would be the best way to implement this situation where an aggregate root is an specialization of another one?
Best Answer
Your gut feeling is right, both Product and PurchasableProduct should be retrieved from the ProductRepository.
This shouldn't be an issue, as one inherits from the other, say we add an extra table to the database with the additional
PurchasableProduct
fields, left join it into theProduct
select statement and then where its not null instead of instantiating and populating a newProduct
, we instantiate a newPurchasableProduct
. You can still return an array/List of Product. when calling the overridden Purchase() method for example thePurchasableProduct
will check its date and theProduct
wont.Essentially your class sub typing is just replacing a conditional statement in the
Purchase
method and some nullable fields on Product. Its neater code, but t shouldn't change the overall flow of the application.If you have a specific need to get only the purchasable products, then you might add a
GetProductsWhichArePurchasable()
method(s) to the repository to allow for optimisation. Whether you still return a list ofProduct
s, orPurchasableProduct
s is interesting. I think a purist would stick with Product, but since your are optimising anyway......