What actually is a “Rich Domain Model”?
Rich Domain Model is what Object Oriented Programming is most suitable for. As we know, OOP helps us, developers, to link behavior (methods) with data (properties) using classes. Unfortunately, often Entities end up as adapters for a database with lots of getters and setters modifying private properties with almost no business logic. It is especially very common when using Active Record pattern, creating MVP product using Rapid Application Development tools and frameworks. And instead of implementing business logic where it would fit the most, in the Model, we end up creating client classes for our domain, where business logic is implemented. Or, especially in web applications, all of the business logic goes directly into a controller, which in my opinion is very harmful. And that is the opposite of a Rich Domain Model, an Anemic Domain Model.
Frameworks encourage us to use Anemic Domain Model concept
That is the biggest problem in my opinion. Often frameworks demand entities to have public getters and setters. And that is not possible when your model is properly designed. How to overcome this?
Most of my time I work with Symfony Framework. It’s very common to use Symfony Forms to validate input from outside of an application. But when you use it directly on an object that you want to create, you need to implement all of these bad-smelling methods starting with get and set, and even allow nulls… That’s not something we want to do.
What we can do? We can create some DTO which will hold the data. No business logic, just a data holder. We validate data using Symfony Forms, put it into DTO, pass it to some factory and voila — object that we want to use in our domain is created! And we are sure it is valid — business rules are validated independently inside a domain model.
What we lose when we do not develop a Rich Domain Model?
A lot. The biggest problem is not guarding your invariants. What is an invariant? It’s some kind of a coneption of a valid state through all of the object life in our system.
What you can see above, is some simple implementation of an Application Client. At first, constructor does not allow creation of an object with a faulty state. Our invariant is that all clients need to have a valid email address and username. Business rules define that username cannot be shorter than 6 characters, and user cannot change it more than 5 times. You should always perform validation in a business objects too, not only at the level of a request validation.
But what about the Email class? It is a Value Object. Main diffferences are between VO and Entities are:
- Entities have some identity and this identity is important.
- Entities are mutable, Value Objects are not. Once created, they cannot change state.
When we compose our domain model of these kind of building blocks, it is much simpler not only to develop the system but also to discuss it with business and react to business changes faster. And also, should class representing Client care about validating email address, extracting domain etc.? No. It is not it’s responsibility. It’s an Email class job.
When using getters and setters you will probably don’t guard your object valid state, which means it can exist and be saved in an invalid state (Corect state of an object should be specified by some business rules implemented directly into a domain model).
There is always a way! Framework is just a tool, you just have to use it the best way possible :).