0
votes

I'm starting a new DDD project and I feel like I haven't really grasped the concepts yet. I have a two aggregate roots in my domain so far, Recipe and Food. Their relationship is like this :

`Recipe`->*`Ingredient`->`Food`

An Ingredient is a quantity + a Food

I think that maybe having the aggregate (Food) in the Recipe aggregate is bad, but it's really convenient because it allows to calculate the nutritional values of a recipe by navigating the relation.

I also do not store the result of the calculation and I recalculate every time so if a food is changed the recipe is updated automatically and I don't know if it's a good idea.

Also, is it Ok to have a single repository for Recipe+Ingredient since they are in the same aggregate and I have to load all the ingredients so I can pass them in the constructor of the recipe anyway?

1
If you are inventing your own project for learning DDD, "editing" is a really lousy domain to start from. Look for something where the model gets to make decisions for itself. - VoiceOfUnreason

1 Answers

4
votes

is it Ok to have a single repository for Recipe+Ingredient since they are in the same aggregate and I have to load all the ingredients so I can pass them in the constructor of the recipe anyway?

That is what most people would expect - a single repository that loads all of the state contained within an aggregate boundary.

I think that maybe having the aggregate (Food) in the Recipe aggregate is bad, but it's really convenient because it allows to calculate the nutritional values of a recipe by navigating the relation.

  • The bad outweighs the good.
  • This is not obvious when using a simplified domain to discover how to DDD.

The motivation for aggregates is to constrain the way the data in your model changes, so that every change leaves the model in an internally consistent state.

If your domain doesn't have any consistency constraints to enforce (for instance, when your data model is really just a database, and the data model has no veto power over proposed changes), then the problem space doesn't offer a lot of guidance in choosing good aggregate boundaries.

The basic principle behind aggregates is that you are creating a sort of data firewall, you never need to worry that changes to this aggregate are going to violate the rules of that aggregate. This implies, among other things, that no two aggregates should ever share mutable data.

Sharing values is fine -- each aggregate gets its own immutable copy of a value, and changing the value in one aggregate doesn't affect the other at all. Sharing entities is bad, because entities are mutable.

Having a model that makes it really convenient to produce a query with the wrong answers isn't as valuable as a model that ensures that the answers are correct.

What will normally happen is that one aggregate will reference another by ID. So your recipe would look like

`Recipe`->*`Ingredient`->`Id<Food>`

Id<Food> is just another immutable value type; the recipe can easily change which food it uses, but it can't change the food in any way. Which in turn means that you never need to worry that changing one Recipe will break another.