0

I understand that objects external to an aggregate shouldn't hold references to entities and value objects belonging to the aggregate. My question if this also applies to the Application Service that exposes the use cases to the client code.

For example, if I have an Order aggregate, and Order Line is a value object inside the aggregate, what would be a more recommended approach:

Option 1: referencing child objects from Application Service

class OrderApplicationService # code ommited def removeOrderLine(order_id, order_line_product_id) order = OrdersRepository.find(order_id) order_line = order.find_line_by_product(order_line_product_id) order.remove_line(order_line) OrdersRepository.save(order) end 

Option 2: using primitive values instead of child objects

class OrderApplicationService # code ommited def removeOrderLine(order_id, order_line_product_id) order = OrdersRepository.find(order_id) order.remove_line_by_product(order_line_product_id) OrdersRepository.save(order) end end 

2 Answers 2

1

This specific fragment demonstrates an anemic domain model

order_line = order.find_line_by_product(order_line_product_id) 

There's no particular reason that the application service should be participating in the business process. Trying to query the aggregate root in this way indicates you are heading down the wrong path.

The most important idea is that the domain model is responsible for interpreting the command; the responsibility of the application (the OrderApplicationService) is just to provide the correct data to the model.

Beyond that, you want the language -- especially within the model, to match the domain language as closely as you can manage.

So this variation is close...

def removeOrderLine(order_id, order_line_product_id) order = OrdersRepository.find(order_id) order.remove_line(order_line_product_id) OrdersRepository.save(order) end 

But either of these might be better, depending on what an order looks like in your domain

"We remove lines from an order"

def removeOrderLine(order_id, line_id) order = OrdersRepository.find(order_id) order.remove_line(line_id) OrdersRepository.save(order) end 

"We remove products from an order"

def removeOrderLine(order_id, product_id) order = OrdersRepository.find(order_id) order.remove_product(product_id) OrdersRepository.save(order) end 
2
  • For operating with value objects inside the aggregate (no id), would it be reasonable to provide the identifying attributes for removal? For example, order.remove_line(product_id) is meant to remove the order line that has a reference to a particular product. Commented Jun 23, 2017 at 20:22
  • You can use also the line index. In fact you use whatever the Aggregate provides but not the line item itself. Commented Jun 24, 2017 at 6:56
0

If you are using "order_line_product_id" which looks like a global identity than it seems to me that your application need to use OrderLine directly and not only via the Order model. If you define Order as just an Entity and not aggregate your life will be simpler and you could create an OrderLine repository and do something like this: OrderLineRepository.delete(order_line_product_id).

If you still want to use the aggregate way than lose the "order_line_product_id" identity and use something like order.remove_line(line_number) notice that line number is only unique per order. So if you want to remove the first line of the order (any order) you call this order.remove_line(1) or the last line order.remove_line(order.lines_count). In that case the unique primary key in your order_lines table should be made from two columns order_id and line_number

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.