0

I have an order aggregate with Order as root having multiple OrderLine. OrderLine has "identity reference" to Product aggregate. But having only "identity reference" is not enough. I need value of "taxable" property, last "price" property for calculating price in OrderLine. In fact, in order to calculate price in OrderLine, some data from Product aggregate is needed. How can this problem is resolved in DDD? Is it valid in DDD approach to have a lite version of Product (ProductLite) to use as read-only DTO?

-- UPDATE (thanks to @Francesc Castells)

// app service to add OrderLine  
product = productRepo.Read(productId)  
orderItemPrice = priceDomainService.CalculatePrice(product.price, product.tax)  
order.AddOrderLine(product.ID, orderItemPrice)  
orderRepo.Save(order)  

enter image description here

Saeed
  • 185
  • 2
  • 13

1 Answers1

2

Yes, this is perfectly valid. An aggregate should store all data that it needs to fulfill its purpose. This data doesn't have to always be user input, it can also come from other aggregates. There's abviously a difference between data produced by an aggregate and data consumed by it. For example, your Order consumes product prices, but it cannot change the product price and expect the rest of the system to respect that new price, as it doesn't own it.

In your scenario, I would say that once in the OrderLine, the Price is not part of the Product anymore, but part of the OrderLine itself, which could probably be defined as the price of the product the moment it was ordered or maybe the moment it was put in the shopping cart.

Francesc Castells
  • 2,692
  • 21
  • 25
  • Yes, you are right about "final" price being part of OrderLine. BUT price in ProductLite is the current price, and the Tax (and probably some discounts) must be applied to it to be final and put in OrderLine. Now, some other questions arised. Who is responsible for creating ProductLite object? Is it OrderRepository itself or Repo must call a domain service to create ProductLite? RepoImpl can read data from product directly or must ask a domain service to do it? – Saeed Jan 09 '23 at 22:40
  • @SaeedD. I don't think the Price in the OrderLine (or ProductLite as you call it) can be the "current price". The Product current price will change eventually, but the Product Price in OrderLine has to stay the same, that's why it's not the ProductPrice anymore, but the OrderLineProductPrice (which is the product price at the time of ordering). It's similar, but from a domain point of view, they are different concepts. Generally, the aggregate root creates its child objects, like Order.AddOrderLine(parameters). In the parameters, you'll pass the product price, tax, etc. – Francesc Castells Jan 10 '23 at 06:38
  • Personally, I wouldn't create a Product Lite entity (I don't think business people would use that term when talking about an order). I'd just put the Price and Tax in the OrderLine, because as I mentioned, they are properties of the OrderLine, not the Product. Repositories should not call domain services nor aggregate operations. A domain service would use a repo to load the aggregate, call an operation on the aggregate and use the repo to store the changes. A domain service can also calculate the discounts if necessary and pass the information to the Order. – Francesc Castells Jan 10 '23 at 06:41
  • I made an update to question, is it what you mean? – Saeed Jan 10 '23 at 09:24
  • OrderLine could have a Product value object, which includes the product id, and some additional details. It won't really be an entity, and it doesn't expose any behaviour. – Alexey Zimarev Jan 10 '23 at 09:34
  • @SaeedD. yes, that looks good to me. – Francesc Castells Jan 10 '23 at 11:09