Although an experimented programmer and architect, the same old basic problem comes back recurrently. I have my own religion about it, but I need some authoritative source.
Are anemic data models ( (c) Martin Fowler?) inherently bad? Should a cake be able to bake itself? Should an invoice know how (and when it should allow) to add lines to itself, or should another layer do that? rabbit.addToHole(hole) or hole.addRabbit(rabbit)? Has it been proved that an ADM is more bug-prone, or easier to maintain, or anything?
You can find a lot of claims on the web, but I'd really want some authoritative quotes, references or facts, if possible from both sides.
See this stackoverflow answer for enlightment.
And this is my opinion:
ADM (Anemic Domain Model) cannot be represented with class diagram UML
Anemic domain model is bad, only in terms of full oop. It is considered as bad design, mainly because you cannot create UML classes and relations with embedded behavior inside it. For example, in your Invoice class with Rich Domain Model (RDM):
The class is self-documented and self explaining about what it can do and what can't.
If it is anemic domain model, it does not has the behavior, and we need to search which class is responsible for Committing and Saving Draft. And since the UML class diagram only shows the relation between each classes (one to many / many to many / aggregate / composite), the relation with service class cannot be documented, and Martin Fowler has his point right.
This is based on class diagram UML in OOAD book by
Lars Mathiassen
. I don't know if newer class diagram UML can represent service class.SRP
In ADM's point of view and compisition over inheritance, RDM (rich domain model) violates SRP. It may be true, but you can refer to this question for discussion.
Shortly, in ADM's point of view, SRP equals one class doing one thing and one thing only.
Any change into the class has one and only one reason.
In RDM's point of view, SRP equals all responsibility related to and only to the interface itself. As soon as the operation involve other class, then the operation need to be put into other interface. The implementation itself may vary, as such if a class can implement 2 or more interfaces. It is simply said as
if an operation in interface need to be changed, it is for and only for one reason
.ADM tend to be abused with static methods and dirty hacks may apply
ADM is very easy to be abused with static methods - service class. It can be done with RDM too, but it need another layer of abstraction and not worth it. Static methods are usually a sign of bad design, it reduced testability and may introduce race conditions, as well as hiding the dependency.
ADM can has many dirty hacks because the operations are not being constrained by the object definition (hey, I can create another class for this!). In hand of bad designer, this can become catastrophic. In RDM it is harder, please read next point for information.
RDM's implementation usually cannot be reused and cannot be mocked. RDM require to know the system's behavior beforehand
Usually RDM's implementation cannot be reused and mocked. In TDD manner, it reduced testability (please correct me if there is RDM which can be mocked and reused). Imagine a situation with this inheritance tree:
If B need logic implemented in C, it cannot be done. Using composition over inheritance, it can be achieved. In RDM, it can be done with a design like this:
In which introduce more inheritance. However, in order to achieve neat design early, you will need to know the system flow firsthand. That said, RDM require you to know the system's behavior before doing any design, or you won't know any of the interfaces named ISubmitable, IUpdateable, ICrushable, IRenderable, ISoluble, etc, suitable for your system.
Conclusion
That's all my opinion about this kind of holy war. Both has pros and cons. I usually go for ADM because it seems like higher flexibility even has less reliability. Regardless of ADM or RDM, if you design your system bad, the maintenance is hard. Any type of chainsaw will only shines when held by skillful carpenter.
I think the accepted answer to this question is one best answering your question too.
Things that I think are esential to remember: