With "Anemic Domain Model" being anti-pattern, why are there so many systems that implement this?
I think there are several reasons
1. Complexity of the system
In a simple system (which is almost all the examples and sample code you find on internet) if I want to implement:
Adding product to Order
I put this function on the Order
public void Order.AddOrderLine(Product product)
{
OrderLines.Add(new OrderLine(product));
}
Nice and super object oriented.
Now let's say that I need to make sure that I need to validate that the product exists in inventory and throw exception if it doesn't.
I can't really put it on Order any longer, since I don't want my order to be dependent on Inventory, so now it needs to go on the service
public void OrderService.AddOrderLine(Order order, Product product)
{
if (!InventoryService.Has(product)
throw new AddProductException
order.AddOrderLine(product);
}
I could also pass IInventoryService to Order.AddOrderLine, which is another option, but that still makes Order dependent on InventoryService.
There is still some functionality in Order.AddOrderLine, but usually it is limited to Order scope, while in my experience there is a lot more Business Logic out of Order scope.
When the system is more then just basic CRUD, you will end up with most of your logic in OrderService and very little in Order.
2. Developer's view of OOP
There are a lot of heated discussions on the internet about which logic should go on entities.
Something like
Order.Save
Should Order know how to save itself or not? Let's say we have repositories for that.
Now can Order add order lines? If I try to make sense of it using simple English, it doesn't really make sense either. User adds Product to Order, so should we do User.AddOrderLineToOrder()? That seems like overkill.
How about OrderService.AddOrderLine(). Now it kinda makes sense!
My understanding of OOP is that for encapsulation you put functions on classes where the function will need to access class's internal state. If I need to access Order.OrderLines collection, I put Order.AddOrderLine() on Order. This way class's internal state doesn't get exposed.
3. IoC Containers
Systems that use IoC containers are usually fully anemic.
It is because you can test your services/repositories which have interfaces, but can't test domain objects (easily), unless you put interfaces on all of them.
Since "IoC" is currently lauded as solution for all your programming problems, a lot of people blindly follow it and this way end up with Anemic Domain Models.
4. OOP is hard, procedural is easy
I have a bit of a "Curse of Knowledge" on this one, but I have discovered that
for newer developers having DTOs and Services is a lot easier than Rich Domain.
Possibly it is because with Rich Domain it is more difficult to know on which classes to put the logic. When to create new classes? Which patterns to use? etc..
With stateless services you just slap it in the service with closest name.
It becomes a 'real' domain model when it contains all (or most) of the behaviour that makes up the business domain (note I'm emphasising business logic, not UI or other orthogonal concerns).
If you're using the Ubiquitous Language, and getting constant feedback from your domain experts, you'll know that you're on the right track (experts should nod when they see your domain model). If you're not doing these things, you're not doing DDD (Eric Evans speak about it).
Onto the point of DTOs: Don't ignore them. From an implementation perspective, you'll need them to ferry data between layers/tiers. How you combine DTOs and true Domain Objects really depends on the technology you're using.
As alluded to in an earlier answer, maybe your focus on data and persistence is distracting you from true domain modelling...
Best Answer
The key question is to ask why is the domain model anemic?
simple structuresdata transfer objects?In any case, if I were to pick a simple rule of thumb for the boundary between domain model logic and service logic, it would be that interacting with related objects is fine within the domain, while accessing the "outside world" (user interface, web services, etc) probably doesn't belong in the domain model.