DDD, PHP. Domain Object and Business Logic


Answers

I'm not sure if there's a right answer for these kind of questions, because applying DDD really depends on the particular domain you're applying it to. There are places where your implementation could be perfectly valid, if it satisfies the business needs. In others, like you mentioned with taxes and the like, it wouldn't. So I'd say that you need to keep asking questions about your domain to fully understand what your needs are before translating them into code.

Having said that, if you have a more complex scenario that requires some extra knowledge of the external world to come up with the value of an invoice, one option would be to represent that in your domain explicitly. In your example, that could be an InvoiceProducer, which could have a signature like:

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 sort of Strategy pattern, which would leave your implementation very similar to the way it is now, but you'd pass with your call 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 specific domain works but, as you can see, the implementation shouldn't be that big of a deal. Is more about how it all fits within your domain.

Question

I've been very busy with trying to understand the concepts of ddd and Model layer lately. Read tons of articles, examples, Q and A's, spent many hours on it. And still I'm not sure if I got some principles right.

One of them is the answer to the question: How much business logic should exist in Domain Objects? Some sources say Domain Objects should be attached with the whole business logic, on the other hand, I came across articles where I assumed it should be as tiny, as possible and only represent its values. It makes me really confused.

In my understanding, domain objects are classes, that represent entities in the domain.

So, lets for example, go with Invoice entity. Each invoice consist of its items. To compute invoice value, we must sum all items values (it's very simple example, in the real world there would be cases like adding tax, computing paid value, etc)

class Invoice
{
    public $id;
    public $items = [];
    public $status;

    const STATUS_PAID = 'paid';
    const STATUS_NOT_PAID = 'not_paid';

    public function isPaid()
    {
        return $this->status == self::STATUS_PAID;
    }

    public function getInvoiceValue()
    {
        $sum = 0;
        foreach($this->items as $item) {
            $sum += $item->value;
        }
        return $sum;
    }
}

In my understanding, method isPaid() is in the right place. It refers to its own data. But I'm not sure about getInvoiceValue(). We operate here on other domain objects.

Maybe we should use domain objects just to represent data only, but use some decorators to perform more advanced tasks?

Thanks in advance.




Tags