Some time ago I found myself arguing with a developer about the approach to take when it came to some design considerations of a microservice solution we were working on. I remember talking about some principles that we needed to consider and trying to inspire my counterpart to adopt a more DDD approach. Although what I was saying made sense in his head, he answered with “this is not a DDD project, this is a microservice project!”
What we were facing were the problems generated by an Anaemic Domain Model. An Anaemic Domain Model is a model with no logic in it. Domain classes look more like a bunch of public setters and getters without domain logic where the client of the class has control over how to instantiate and modify the class. In these models, the client has to interpret the class purpose and use. Usually, the logic has been pushed to other classes called something like services, helper or manager plus the name of the domain class. With the logic sitting in another class, there is nothing that helps the client to navigate or use the model class.
This approach leads to many design problems and disadvantages:
A good way to address the main disadvantages of Anaemic Domain Models is implementing a Rich Domain Model. The main difference with an Anaemic Domain Model is that our domain logic is part of our domain entities, data and behaviour sit together. That logic guides and controls how the entity is instantiated, validated and operated, preventing the client from having entities with an inconsistent state.
There is a long list of best practices you can use to avoid an Anaemic Domain Model, also it depends on the complexity of the solution and on how purist you want to go when comes to enriching your model. The objective of this post is not to present a complete study or exhaustive list of best practices, but here are a few of hints and tips to get you started:
In summary, embrace encapsulation! It is always a good practice to assign the responsibility of maintaining data integrity to the objects that contain that data.
Martin Fowler wrote an article in 2003 where he classified the Anaemic Domain Model as an anti-pattern. Since then, the Anaemic Domain Model has been widely recognised as an anti-pattern and even though it is not uncommon to find many projects using it, especially in combination with the three-layer architecture. I think that there are a couple of reasons for this:
Since I read Domain-Driven Design by Eric Evans a long time ago, I always try to follow and apply some of the principles and patterns described in the book to my designs. However, from time to time I wonder if I put excessive emphasis and effort on it.
Sometimes the complexity of the project simply doesn’t invite to apply many of the principles, sometimes we can deliver and comply with our requirements with less complexity, and sometimes you simply YAGNI. If your solution mainly contains CRUD operations without business logic embedded, you are unlikely to need DDD.
Going back to “this is not a DDD project, this is a microservice project!”. We were already facing many of the drawbacks of having an Anaemic Domain Model. Also, many concepts and patterns described in Domain-Driven Design from Eric Evans still apply to microservices architectures, even when those principles were described way before microservices were recognised as the architecture that nowadays is being used in the industry. A couple of good examples of these principles are the use of the notion of Bounded Context to help determine the domain of a microservice and making your code speak the ubiquitous language. The ubiquitous language is going to ease communication and give a stronger sense of purpose to your domain design. Developers should not be unaware of business language.