Well since I will begin designing my own object relational mapping system soon, I decide to take a tour on Doctrine and see how it is implemented(though mine will not use annotation, so theres a big difference). However, for all the tutorials/articles I can find on the web, they all use anemic domain models. In other words, the entity objects each article create do not have any business logic other than constructors and getters/setters. As far as I know, anemic domain model is an anti-pattern, its to be avoided at any costs unless you are working on legacy code. So I wonder, does Doctrine’s ORM lead to anemic domain model design? Or is it the tutorials/articles authors who made a mistake designing anemic domain models? What do you think?
No. Doctrine 2 does not give rise to anemic domain model designs.
If you explore the D2 code you will find that D2 uses reflection to hydrate and persist it’s entities. No getters/setters are required for D2 to work it’s magic. Not even the constructor is called. D2 does not care how you model your domain.
It’s also fairly easy to structure an application to make the domain model independent of the persistent layer.
Model\\Product.php Model\\ProductInterface.php Model\\ProductRepositoryInterface.php Doctrine\\Product.php (extends Model\\Product) Doctrine\\ProductRepository (implements Model\\ProductRepositoryInterface InMemory\\ProductRepository (implements Model\\ProductRepositoryInterface)
You can pretty much do whatever you want in the Domain Model layer. And then persist with Doctrine or pretty much any other reasonable persistence library. You can even chose which library to use at run time.
The anemic domain model is basically a result of the types of applications which end up being implemented in PHP. PHP is mostly used for CRUD type applications. Business logic is usually stored in services of some sort.
Here is the challenge: Post a link to any PHP application out there that does NOT use an anemic domain data model. I have seen one or two very simple examples but nothing in production.
I would say the opposite. All an ORM like Doctrine does is decouple (and simplify) the persistence mechanisms in your code, allowing your domain model to be more about model and less about controller code, much the way templates influence developers to keep markup out of model logic.
I see, so its not doctrine’s fault that many PHP applications use anemic domain model. Thanks for the clarification, I really appreciate it. I dont think its impossible to design a PHP application without using anemic domain model. All you have to do is to either completely stop using domain services thus moving all business logic back to the model, or move part of the service layer functionality into domain model. I personally prefer the latter, as some service methods are highly specific and even tied to the application layer.
According to the DDD books, its possible to maintain non-anemic domain models even if you still have a service layer. I know its hard to accomplish since sometimes its unclear where to draw the line, but its still doable. The fact that many PHP applications use anemic domain models is just so wrong, its an anti-pattern and should be avoided unless there is really no business logic at all in your application(it may happen, but rare). If the business logic is stored in service layer, theres always a way to refactor your application into non-anemic domain model design.
Now I guess a better topic is whether excessive usage of service objects result in anemic domain models?
If only we had an example of a PHP application with a non-anemic domain model. Even one example would lend considerable credibility to My Lord’s assertion that anemic models are an anti-pattern in PHP.
I will be designing my application to use rich domain models, so I will prove to you how it can be done after it completes. And a minor correction, anemic domain model is not just an anti-pattern in PHP, its an anti-pattern no matter what programming language(s) you use.
I think you need to take it on a case-by-case basis. Sometimes there are good reasons for moving functionality into a service. For example, normally we might think that the job of hashing a user’s password belongs in the User class. But as we add variations of that task (straight hash vs PBKDF2 vs bcrypt vs scrypt vs others), then it starts to make sense to refactor those variations into services that we can inject. The User class just became slightly more anemic – but for good reason. Obviously don’t move logic into a service for no reason at all, but don’t be afraid of using services either.