I have been struggling with this concept in the context of web applications ever since I first read about it. The theory states that the domain objects should encapsulate their behaviour and business logic. A model which contains only data and has its logic somewhere outside is called an "anemic domain model", which is a bad thing. Also, the domain should not perform data access.
If for instance I had a social app which had a bunch of objects of type User, and users should be able to add other users as their friends, the User class should contain a method named Befriend(User user) so that I could do something like userA.Befriend(userB).
class User {
Friends[] friends;
void Befriend(User user) { ... }
}
However, the act of befriending might contain some restrictions and so I would have to do some validation in my Befriend method. Here are some purely theoretical restrictions:
- The user must not already be your friend
- You and the other user must not have common friends
- In Bucharest it must be raining
Now let's imagine that the friends lists might be huge, userA might have 50.000 friends, userB might have 100.000 friends. So, for validating 1 and 2 it wouldn't be efficient to eagerly pull the entire friends lists from the database when constructing the user object and then doing those checks in my Befriend method iterating the friends list. In the database I have indexes and checks like these would be trivial (and fast). So naturally I would prefer to put these queries somewhere in my Data Access Layer and use them whenever needed.
class FriendsRepository: IFriendsRepository {
bool HasFriend(User user, User friend);
bool HasCommonFriends(User userA, User userB);
}
But how am I supposed to use this object inside my Befriend method from my User object? People say domain objects must not use repositories (even through abstractions such as interfaces), though there seems to be some disagreement here. Say I violated this rule. Domain objects don't benefit from Dependency Injection so I would have to change my Befriend method to:
void Befriend(User user, IFriendsRepository friendsRepository) { ... }
Alright. Now what about the weather? That's something completely unrelated to our entity and that information comes from an IWeatherService. Again, I need it in my Befriend method.
void Befriend(User user, IFriendsRepository friendsRepository, IWeatherService weatherService) { ... }
This already makes me feel like this method does not belong inside the User class. I have a lot of external dependencies and I don't get Dependency Injection which sucks. But pulling this out from the User to a service (or whatever) inside my Application Layer makes my domain model anemic. I very rarely encountered methods which could either be executed without validation or contain only extremely simple validation rules, only depending on the immediately available properties on the said entity (like primitive fields for instance, such as Username string, ActiveUntil date etc.).
So I'm left asking: what kind of methods could naturally fit in the domain objects? Let's be honest, real apps often deal with huge amounts of data, many object relations and very complex validation logic. Rarely you only have to do trivial checks like "is this user over 12 years old?".
P.S.: I used that example purely for demonstration purposes. Please don't cling on it.