DDD, PHP. Domain Object and Business Logic

I have been very busy trying to understand the concepts of ddd and Model recently. Read tons of articles, examples, Q and A, spent many hours on it. And yet 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 that domain objects should be tied to all business logic, on the other hand, I came across articles where, as I assumed, it should be as tiny as possible and represent only its values. It bothers me.

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

So, for example, let's move on to the invoice object. Each invoice consists of its elements. To calculate the cost of an account, we must summarize all the values ​​of the elements (this is a very simple example, in the real world there will be cases, such as adding tax, estimated 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, the isPaid () method is in the right place. This refers to his own data. But I'm not sure about getInvoiceValue (). We work here on other domain objects.

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

Thanks in advance.

+5
source share
3 answers

How much business logic must exist in Domain objects? [...] I came across articles where, as I thought, it should be as small as possible and represent only its values.

Beware of the Anemic Domain Model , which is almost exclusively composed of data and has no behavior. DDD is the creation of a behavioral domain model. Thus, it’s great to add logic to the domain classes.

DDD emphasizes good object-oriented design, sharing of methods and data, thereby contributing to high cohesive systems .

+3
source

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.

+3
source

How much business logic must exist in Domain objects?

All this (if possible). The DDD point is the capture of your business logic in your domain - various tactical templates can be used here (aggregates, objects, value objects, domain services, etc.).

Domain objects are classes that represent objects in a domain.

Classes in your domain can represent more than objects. Aggregated objects, objects, value objects, domain services, etc. May be represented by classes in your domain.

But I'm not sure about getInvoiceValue (). We work here on other domain objects.

The example you give for an invoice is a classic example of Aggregate-Invoice that will contain InvoiceItems. getInvoiceValue () is excellent here.

In our case, the invoice is an aggregate. The aggregate root is the Invoice itself, but it is also the Essence, right? It has its own identification (invoice number, which is unique).

Yes, right

What is InvoiceItems? Can I get them directly from the InvoiceItem repository (if I have to create one), or do I always have to work only on Aggregate?

It depends on your use case. This helps to separate write and read models (CQRS). If you are talking about the reading side (i.e., Reporting), you bypass the domain model and have objects that represent your reading model. It could just be a database query. If you are talking about your side of the record (i.e., about commands, domain), then usually you should have a repository for each placeholder root. What are your common roots? This is a matter of modeling. You want to create them so that they apply all your business rules - in your example, if the Account needs to download InvoiceItems to ensure compliance with the rules (for example, "no more than 5 pieces per invoice"), then yes, they must be loaded through cumulative root

+2
source

All Articles