Comment by valenterry
9 months ago
I'll reply here with a very quick example why the anemic domain model is superior in general, no matter if you do OOP or anything else.
You used the example of an "order" yourself, so I'll built upon it.
I would never combine functionality to update an order with the data and structure of the an order. The reason is simple: the business constraints don't always live inside the order.
Here's an example why such an approach inevitably must fail: if the business says that orders can only be made until 10000 items have been ordered in a month, then you cannot model that constraint inside of the order class. You must move it outside - to the entity that knows about all the orders in the system. That would be the OrderRepository or however you want to call it.
Remember, here is what you said in your other post:
> Your Order class has a collection of Product items, but you can update an order, cancel a order, repeat an order, etc. This behavior should be member functions.
So your Order should have a repeat function? But how can the order know if it can be repeated? It might violate the max-monthly-items constraint. The only way for the Order to do it is to hold a reference to the OrderRepository.
And this is a big problem. You have now entangled the concept of an OrderRepository and of an Order. In fact, Orders could totally live without an OrderRepository alltogether, for example when you build an OrderSimulation where no orders are actually being executed/persisted. But to do so, now you have this OrderRepository, even if you don't need it.
The rule of the thumb is: if the business says "we don't need feature A anymore, remove it" then you should be able to remove that feature from the code without touching any unrelated feature. If you now remove the OrderRepository and cause a bug in the Order class due to your code changes, the business will probably wonder how that could be, because while the OrderRepository cannot exist without Orders, Orders can exist without an OrderRepository.
And if that seems a bit unrealistic, think of users: A user can easily exist without a UserRepository, but not the other way around.
That makes clear, that you the rich domain model is an unsuitable and generally suboptimal solution to modeling the domain of a business. The anemic domain model on the other hand matches it perfectly.
And one more thing: even natural language disagrees with the rich domain model. Does an order repeat itself? No! An order is repeated and that is, it is repeated by something or someone. This alone makes clear that there is an entity beyond the Order that is responsible for such action. And again, the anemic domain model is a great solution for expressing this in code.
But if you disagree, I'd like you to explain what you believe the disadvantages of the anemic domain model are.
You made a great example here and I absolutely agree with you.
In fact I find this type of accidental / unneeded coupling is the number one cause of problems, bugs and limitations of re-use and thus development velocity in any software product. Concepts where a single way dependency is turned into a cycling dependency are really hard to evolve, maintain, test and understand.
In fact I'd go as far as to say that as a general rule of thumb if you have a situation where your class A depends on class B that depends on class A you've made a big doo doo and you should really seriously re-consider your design.
(Adjacent to this rule is that classes that exist in the same level of of the software hierarchy and are thus siblings should also not know about each other).
In fact when you structure your code so that the dependencies only go one way you end up with a neat lasagna code base and everything can easily slotted in. (Combined with this a secondary feature which is to eliminate all jumps upwards in the stack, i.e. callbacks)
> I would never combine functionality to update an order with the data and structure of the an order. The reason is simple: the business constraints don't always live inside the order.
> Here's an example why such an approach inevitably must fail: if the business says that orders can only be made until 10000 items have been ordered in a month, then you cannot model that constraint inside of the order class. You must move it outside - to the entity that knows about all the orders in the system. That would be the OrderRepository or however you want to call it.
It's not that hard.
If the constraint of your example is a domain constraint, and so it's *always* valid, then when you hydrate an Order entity, other than the Order data itself, you also need to provide the total number of orders.
```
// orders is the repository, and it hydrates the entity with the total number of orders for this month
order = orders.new()
// Apply the check
order.canBeCreated()
```
Where the `canBeCreated` method is as simple as:
```
if this.ordersInAMonth > TOTAL_NUMBER_OF_ORDER_IN_A_MONTH ...
```
Fixed.
It's the same as using the OrdersRepository to query the number of orders directly before creating one, but here, the logic is just in the class.
Now, your example is pretty stupid, so I know it must not be taken literally but...
PS: I'm all but not a DDD advocate.
I would say that this code is not good (or to be more diplomatic: not optimal). Firstly, because between `order = orders.new()` and `order.canBeCreated()`, it's possible to insert other calls or actions on or with that order, which should actually not be allowed/possible.
And second, because my original critics still holds: you now have some kind of order-entity-unrelated information inside the order (or inside its `canBeCreated()`). This will force you to touch the order-entity when removing a business constrain that is unrelated to the (single) order-entity. Because otherwise, where does "ordersInAMonth" come from? It must be able to talk to the database or something.
> Now, your example is pretty stupid, so I know it must not be taken literally but...
No no, you absolutely can take it literal. It might not be very realistic, but that doesn't change the fact that we can use it to discuss pros and cons of different designs.
> It's the same as using the OrdersRepository to query the number of orders directly before creating one, but here, the logic is just in the class.
As with my two issues that I mentioned above, the problem is the "just" in your sentence. It appears that your assessment is that the code living in a different place is merely a problem of the code being in a different place with no effect on productivity. But to me, having the code in a "wrong" place becomes a really big problem over time, especially in a big code base.
Also, we can extend this example. Let's say we have two or more entities. Like orders, users and stores and there are constraints that span and impact the state and/or creation of all of them at the same time.
Now let's compare the different approaches of us. In my case, it's rather easy: there must be some "system" or "service" the lives above all the entities that are constrained by a business rule. So if there is a business rule that touches entities A, B and C, then there must be some "system" or "service" that knows about all A, B and C and can control each of them. In other words, there cannot be a "system" or "service" that controls just A anymore. The logic to ensure the constraint then lives in that service.
With your approach, how and where do you put the code for that constraint?
And let's, just for the sake of the argument, assume that you cannot push the constraint into the database. Because that basically would be such an uber-service as described by me above. In reality, we might employ the database to (also) enforce constraints. But for the sake of the discussion, let's say we use a database where we cannot.
Looking forward to your response!
Links to any existing articles on this topic would be greatly appreciated.
I highly recommend a video if you don't mind the format: https://youtu.be/zHiWqnTWsn4?t=3134
The slide at 52:14 is on the SOLID principles, the first one is on SRP which gives pretty understandable advice about whether Order should have behaviours.
This is the original article from Fowler: https://martinfowler.com/bliki/AnemicDomainModel.html
By searching for that term, you'll easily find lots of other takes on the matter.
> I'll reply here with a very quick example why the anemic domain model is superior in general, no matter if you do OOP or anything else.
I can search for it of course but results that aren't about OOP purism appear to be rare.