DDD – How to Design Aggregate Inheritance and Repositories in Domain-Driven Design

aggregatedomain-driven-designrepository-pattern

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 the Product select statement and then where its not null instead of instantiating and populating a new Product, we instantiate a new PurchasableProduct. You can still return an array/List of Product. when calling the overridden Purchase() method for example the PurchasableProduct will check its date and the Product 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 of Products, or PurchasableProducts is interesting. I think a purist would stick with Product, but since your are optimising anyway......

Related Topic