0

I'm still learning DDD and trying to model in my domain layer a Product that is made by none or many Materials in different quantities.

Initially I create two aggregates, Product and Material and one entity ProductMaterial. Product has a list of ProductMaterial (that has the MaterialId and Quantity) and methods to add, remove and update Material, also, a product can't have duplicated ProductMaterial.

To persiste the aggregate Product I use the repository, not a problem, but know how I deal with the following cases?

1 - How my repository will know a ProductMaterial was removed/updated from Product?

2 - My Product is made up by, lets say, 10000 ProductMaterial, and I need just to change the name of the Product, for this I have to load all the ProductMaterial?

I'm probably missing some concepts or not understanding the real problem of the domain, can someone help me?

3 Answers 3

1

DDD is a technique for solving complex business problems.

When creating a domain model, forget about the relational database.

Start with drawing a model on a whiteboard together with a domain expert. This version will definitely not have a concept called ProductMaterial. It will contain a line between Product and Material with a note that every Product has various materials with different quantities. Thoroughly ask questions about this relationship; is there a name for this concept? Perhaps Bill of Materials? What kind of products have 0 materials? Should we treat those differently? Is that perhaps a related but different concept in the model? Etc. Etc.

Next convert this model to code, again without considering the relational database. The Product Aggregate Root could contain the Bill of Materials concept, which could be a ValueObject that contains the information about the amount of materials. ValueObjects are immutable, so when the bill of materials for a product changes, it is replaced with a new instance. It’s also possible that it turns out the bill of materials should be promoted to an Aggregate Root. It would get it’s own Id and has a ProductId in order to connect the two aggregates. Domains can be modeled in different ways and it’s your job, together with domain experts, to find the model that best fits the needs for the business.

Eventually you’ll want to store the aggregate in a database.

If you use a NoSQL document db, simply store an aggregate as document. More advanced systems combine this with specialized read databases, that get updated with domain events.

If you prefer a relational database, you’ll have to create a mapping between the domain model en the relational model. ORMs can help with that, but be careful not to ‘leak’ the database details into the domain layer. The idea is to keep the domain model focused on the business needs, not the technical details. One of the big advantages of using an ORM is that you don’t have to write your own change tracking. If you have an aggregate that contains a collection of entities, it can be a daunting task to detect changes to those entities.

You also asked about loading a lot of data when you want to change an unrelated property in an aggregate. Should you still load all data? The answer is yes, the caller of a domain method should not have (implicit) knowledge about what parts of an aggregate should be loaded. An aggregate should always be fully loaded. For this reason aggregates should be kept small. An ORM can also assist in this scenario if it provides lazy loading. From a purely technical point of view, the aggregate is not always fully loaded when using lazy loading, but from a practical point of view, as soon as the data is actually used, it will be available. The client of the aggregate can simply assume the aggregate is fully loaded.

3
  • Very enlightening answer. For me it's hard to forget about the database and it ends up polluting my way of thinking about the domain. Thanks very much. Apr 30, 2022 at 15:47
  • I have one question about what you sad here: "It will contain a line between Product and Material" ... "Thoroughly ask questions about this relationship; is there a name for this concept? Perhaps Bill of Materials?". In the case the concept discovered is Bill of Materials, now the line between Product and Material is removed and Product has a line that connect to Bill of Materials. Material now has no lines at all? Apr 30, 2022 at 17:29
  • Bill of Materials combines amounts with Material Ids. Material also has a Material Id. Make that relation visible by drawing MaterialId on the whiteboard and connect that to both concepts. This creates an opportunity to discuss this concept. Does it make sense to call it a Material Id? It’s not unlikely that the domain expert says “I know what you mean, but we call this xyz “.
    – Rik D
    Apr 30, 2022 at 20:11
0

How my repository will kow a ProductMaterial was removed/updated from a product

By executing a database query, or employing some form of change tracking. If you need to make sure a database response is still accurate, just read it from the database again.

My Product is made up by, lets say, 10000 ProductMaterial, and I need just to change the name of the Product, for this I have to load all the ProductMaterial?

No. In a properly architected database, the Product name would be changed in one place: the Product record.

0

Let's answer the question parts one by one.

How my repository will know a ProductMaterial was removed/updated from Product?

The Repository is responsible for persisting an Entity or a Value Object; It shouldn't know business rules and invariants. Your question could be done by creating a Domain Service like IProductAndMaterialService. The implementation uses a repository to do its job. The methods inside the DomainService could be RemoveMaterialFromProduct(int materialId, intProductId), UpdateMaterialFromProduct(int materialId, int ProductId).

My Product is made up by, lets say, 10000 ProductMaterial, and I need just to change the name of the Product, for this I have to load all the ProductMaterial?

In this case, a use-case handler (Application Layer) or a Domain Service can do the scenario without loading unnecessary related objects.

All in all, Domain Services and use-case handlers employ Repositories and Domain Model to handle use-cases.

3
  • Thanks for your reply. So I think a Domain Service is needed in this situation. I'll try and see if it works. Apr 29, 2022 at 16:32
  • A Domain Service can be used to add behavior to a model that cannot logically be placed in an entity. Overuse of domain services leads to anemic models.
    – Rik D
    Apr 29, 2022 at 20:08
  • @RikD, the questioner clearly stated that there are two aggregates for two concepts in the Domain Model. In this situation, Domain Service is an appropriate choice. It seems that there is no place for the functionality in both Entities. Apr 30, 2022 at 3:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.