In a DDD implementation, I am trying to design my domain model and the way to properly interact with child entities of an Aggregate through the Aggregate root.
Imagine the following two classes in an Aggragate called "Basket".
class Basket
{
string Id;
int TotalPrice;
List<Item> Items;
}
class Item
{
string Id;
string BasketId;
int Price;
}
The Basket is the Aggregate root and the Item is a child entity, with one-to-many relationship.
Now, at some point the price for a specific Item might change, so I want to update the Price for this Item and also update the TotalPrice of the Basket that contains that item. I am following the rule that any "change" on a child entity should "pass" through the Aggregate root. So, imagine that Basket has a UpdateItemPrice(string itemId, int newPrice) method and Item has a UpdatePrice(int newPrice) method, that each does what their name describes. My question is, which of the following implementations is more proper?
Approach 1:
In class Basket:
void UpdateItemPrice(string itemId, int newPrice)
{
var itemToUpdate = Items.Where(i => i.Id.Equals(itemId)).FirstOrDefault();
if(itemToUpdate != null){
var oldPrice = itemToUpdate.Price;
itemToUpdate.UpdatePrice(newPrice);
TotalPrice = TotalPrice - oldPrice + newPrice;
}
}
And in class Item:
void UpdatePrice(int newPrice)
{
Price = newPrice;
}
Approach 2:
In class Basket:
void UpdateItemPrice(string itemId, int newPrice)
{
var itemToUpdate = Items.Where(i => i.Id.Equals(itemId)).FirstOrDefault();
if(itemToUpdate != null){
itemToUpdate.UpdatePrice(newPrice);
}
}
In class Item:
void UpdatePrice(int newPrice)
{
var oldPrice = Price;
Price = newPrice;
if(Price != oldPrice)
{
// raise a ItemPriceChangedDomainEvent
}
}
In the first approach, what is happening is obvious. In Basket.UpdateItemPrice method, we update the Item's price through the Item.UpdatePrice method, and then we immediately calculate the new TotalPrice of the Basket there.
In the second approach, "changes" between the entities are propagated in a more "eventual" way. In Basket.UpdateItemPrice method we also update the Item's price through Item.UpdatePrice method, but then inside there we also raise an ItemPriceChangedDomainEvent, and do no TotalPrice calculation in Basket.UpdateItemPrice. What would happen next is that an ItemChangedDomainEventHandler will be invoked, that will find the Basket that contains the Item which's Price has changed through the repository, and then call a CalculateTotalPrice() method on the Basket (note: Domain Event handler and Basket.CalculateTotalPrice() method are not shown for simplicity).
The first approach is much simpler and straight forward than the second one, especially for my case where I only want to calculate a new TotalPrice after the Item's Price update. But, the second approach might be more scalable and flexible for future extension of the application, if for a example besides calculating the new TotalPrice, I also wanted to check if the Item's new price exceeds a theshold and then also raise an ItemPromotedForDiscountDomainEvent, which when handled will apply also a discount on the Basket's TotalPrice.
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?
Thanks in advance.
updateItemPrice
?