So, after all this talking, could someone give me a thought on which of these two approaches is more suitable/appropriate for me to follow?
Ruth Malan wrote: Design is what we do when we want to get more of what we want than we would get by just doing it.
One implication is that you have to have a good sense for the "what you want" if you hope to achieve a good design.
With "objects" -- in the Java circa 2003 sense of the term -- a common want is information hiding. In other words, we want a design that minimizes the blast radius of a decision that we might change later.
That usually means that each object gets to update the information in its own data structures.
Here, that probably means something like:
void Basket::UpdateItemPrice(string itemId, int newPrice)
{
var itemToUpdate = Items.Where(i => i.Id.Equals(itemId)).FirstOrDefault();
if(itemToUpdate != null){
itemToUpdate.UpdatePrice(newPrice);
Total = CalculateTotalPrice()
}
}
or even
void Basket::UpdateItemPrice(string itemId, int newPrice)
{
Items
.Where(i => i.Id.Equals(itemId))
.ForEach(
itemToUpdate => itemToUpdate.UpdatePrice(newPrice)
)
Total = CalculateTotalPrice()
}
Minimize control flow complexity and "area under ifs", favoring consistent execution paths and times over "optimally" avoiding unnecessary work. -- John Carmack, 2007.
Another consideration in domain code: it should ultimately look like it was written by a domain expert. So we might even want the plumbing to be simpler
void Basket::UpdateItemPrice(string itemId, int newPrice)
{
var item = find(itemId)
item.UpdatePrice(newPrice)
Total = CalculateTotalPrice()
}
// raise a ItemPriceChangedDomainEvent
You won't normally see this to communicate across entity boundaries within the same "aggregate". Remember, an aggregate is "a cluster of related objects that we treat as a unit for the purpose of data changes". If our entities are together part of the same unit, they really shouldn't need to communicate via "domain events".