I am not sure if there is a correct answer to these questions, because the use of DDD really depends on the specific domain to which you apply it. There are places where your implementation can be absolutely effective if it meets the needs of the business. In others, as you mentioned with taxes, etc., this is not so. Therefore, I would say that you need to constantly ask questions about your domain in order to fully understand what your needs are before translating them into code.
Having said that, if you have a more complex scenario that requires additional knowledge about the outside world in order to come up with the value of the invoice, one of the options would be to explicitly represent this in your domain. In your example, it could be an InvoiceProducer, which can have a signature, for example:
class InvoiceProducer { public function __construct(TaxProvider $taxProvider) { $this->taxProvider = $taxProvider; } public function invoiceFor(array $items) { new Invoice($items, $this->calculateValue($items)); } private function calculateValue(array $items) { $sum = array_reduce($items, function($acc, $item){ $acc += $item->value; } return $this->taxProvider->applyTaxTo($sum); } }
Another option would be to use some kind of strategy template that would leave your implementation very similar to what it is now, but you will go through with your challenge the way you want the taxation to be calculated:
public function getInvoiceValue(TaxProvider $taxProvider) { $sum = 0; foreach($this->items as $item) { $sum += $item->value; } return $taxProvider->applyTaxFor($sum); }
Again, it really depends on how your particular domain works, but as you can see, the implementation doesn't have to be that big. Learn more about how it all fits in your domain.
source share