anemic data model, dao's, … authoritative refe

2019-07-11 11:43发布

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.

2条回答
甜甜的少女心
2楼-- · 2019-07-11 12:22

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):

  • Class Name: Order
  • Implemented: ICommittable, IDraftable, ...
  • Attributes: No, UserId, TotalAmount, ...
  • Behavior: Commit(), SaveDraft(), ...

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.

In general, the more behavior you find in the services, the more likely you are to be robbing yourself of the benefits of a domain model. If all your logic is in services, you've robbed yourself blind.

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:

    A
   / \
  B   C

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:

    A
    |
    D
   / \
  B   C

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.

查看更多
别忘想泡老子
3楼-- · 2019-07-11 12:32

I think the accepted answer to this question is one best answering your question too.

Things that I think are esential to remember:

  • ADM is adequate for CRUD applications, and since most apps start out this way, it's OK as a starting architecture; you can evolve from there via refactoring, if needed, but there's no point of over-designing an application right from the start
  • once complexity starts to grow - once business rules start to pile up - it's less convenient to keep the model anemic - separating the rules from the objects they act upon makes it hard to remember all rules that apply when you look at the object
  • if the rules are in the domain objects, they are also conducive to writing tests, if they're elsewhere (say in stateless services), you don't know what a domain object can do and what all constraints that apply to it are, to write proper tests for it (think orthogonal rules modelled in distinct services)
  • there's a distinction to be made between really simple applications and anemic domain models: in a really simple application, there is not much business logic, in an anemic domain model the logic exists, but is kept separately from the domain model
查看更多
登录 后发表回答