Short format of question
Is it within best practices of DDD and OOP to inject services on entity method calls?
Long format example
Let's say we have the classic Order-LineItems case in DDD, where we have a Domain Entity called an Order, which also acts as the Aggregate Root, and that Entity is comprised not only of it's Value Objects, but also a collection of Line Item Entities.
Suppose we want fluent syntax in our application, so that we can do something like this (noting the syntax in line 2, where we call the getLineItems
method):
$order = $orderService->getOrderByID($orderID);
foreach($order->getLineItems($orderService) as $lineItem) {
...
}
We don't want to inject any sort of LineItemRepository into the OrderEntity, as that is a violation of several principles I can think of. But, the fluency of the syntax is something that we really want, because it's easy to read and maintain, as well as test.
Consider the following code, noting the method getLineItems
in OrderEntity
:
interface IOrderService {
public function getOrderByID($orderID) : OrderEntity;
public function getLineItems(OrderEntity $orderEntity) : LineItemCollection;
}
class OrderService implements IOrderService {
private $orderRepository;
private $lineItemRepository;
public function __construct(IOrderRepository $orderRepository, ILineItemRepository $lineItemRepository) {
$this->orderRepository = $orderRepository;
$this->lineItemRepository = $lineItemRepository;
}
public function getOrderByID($orderID) : OrderEntity {
return $this->orderRepository->getByID($orderID);
}
public function getLineItems(OrderEntity $orderEntity) : LineItemCollection {
return $this->lineItemRepository->getLineItemsByOrderID($orderEntity->ID());
}
}
class OrderEntity {
private $ID;
private $lineItems;
public function getLineItems(IOrderServiceInternal $orderService) {
if(!is_null($this->lineItems)) {
$this->lineItems = $orderService->getLineItems($this);
}
return $this->lineItems;
}
}
Is that the accepted way of implementing fluent syntax in Entities without violating core principles of DDD and OOP? To me it seems fine, as we're only exposing the service layer, not the infrastructure layer (that is nested within the service)