The Rich Domain Model is the technical part of Domain Driven Design. It is composed of many building blocks, but I would like to present a different view of the model. This is just one of many in a series of articles on this topic.
Deep Systems
The data model largely determines the application architecture. With simple CRUD applications you can often assume the colloquial assumption of “get entity and push”. In this case, any data model, service layer that manages that data, and an API interface would be enough.
Unfortunately, in the case of the so-called deep systems, i.e. those that cannot be specified by the user interface, what you can see on the UI does not necessarily reflect the actions that are performed in the system. Business logic, integrations, schedulers, and a lot of background actions are performed underneath. In this case, using a primitive approach that appears in most of the tutorials ends up with spaghetti code, developer’s sadness, and therefore — an unsustainable system. How to approach it consciously and wisely?
The anemic data model was defined in 2003 by Martin Fowler as an anti-pattern. He emphasizes that this is not consistent with object-oriented programming, but rather with procedural programming.
“The anemic domain model is really just a procedural style design…”
But what exactly is this anemic thing…?
Primitive model
Oversized entities that typically generalize the model, usually called an anemic model full of thoughtless getters and setters, which does not reflect the business, but the data structure — a table from SQL. The problem does not necessarily have to be with relational database applications. In the world of fashionable (and how wonderful) NoSQL databases, such an entity may look almost identical to an SQL entity, except that instead of a reference, we will see nested models, which results in a JSON database with the size of the Old Testament (golden class).
Therefore, we should remember that there are no better or worse databases, they are simply better or worse when used in a given case.
public class Order {
private Long orderId;
private LocalDate orderDate;
private LocalDate orderShippedDate;
private String orderStatusCode;
private int orderTotal;
private Long customerId;
private String shipToName;
private Long shipToAddressId;
private String shipToPhoneNumber;
private Long shippingOptionId;
private Long paymentOptionId;
// getters and setters
}
This is an example of real code used in the application — is there anything good here? Not…
Let’s start with the basics.
If you are calling two setters in a row, you are missing a concept (Oliver Gierke)
When learning a programming language, we always talk about encapsulation. We want to prevent data changes. However, if you do not define class behavior, how do you want to change its state? Of course I forgot, we have setters. Why do we have them? After all, we can use public fields, it is no different, and there is no encapsulation in both cases :( The class can do anything and everyone can manipulate the class (there is no encapsulation or limited responsibility here).
Another problem? Over-generalization and lack of context in which the given domain of the model operates.
Understand how business works.
In the previous model, order contexts were mixed up, which may possibly have different statuses with dozens of dependencies. In fact, the order is evolving like in the chain above. We can assume that when ordering goods, we must make an initial reservation of inventory, process a given request, and when it goes to realization, we can then talk about an order. When the order is completed, we will want to pack it and send it to the warehouse so that it can be shipped. In addition, we will generate an invoice for the order, which will be sent to the customer. Each of these contexts has its own fields, conditions, logic, and encapsulation.
AVOID Entity Services by Focusing on Capabilities
So, what are the effects of the anemic model?
- problems with maintaining / developing the application, fuzzy logic goes beyond the domain, many dependencies — large coupling
- worse performance due to operating on the so-called golden class
- complicated logic often operating on many booleans or enums
- over-generalization - no context in which a given domain of the model operates
- the class can do anything and everyone can manipulate the class
- breaking the Liskov rule — extracting an object using .get().get()
- SET intention unknown:
What business action is taking place here? — We do not know because we have not named it
And when will I want to use such a method? Am I not confusing anything?
In two months, will I even know what’s going on here?
What if I forget to set some more values?
Someone else manages the entity, tells it how to live and what to do, usually some SuperUtilService, MyFancyExecutionManagerService — such incapacitation :)
Rich model
- the method expresses its intentions
- business language goes into code
- the entity is immutable, there are no SETs, just a public method that does whatever it has to do
- methods on objects can be reused without fear that we have additionally forgotten something
- the entity manages itself, only it can change its state, verify it and react — it is mature and independent
Domain Driven Design philosophy fits perfectly into the concept of a rich domain. Below I would like to present a project based on DDD:
Project of a library using Domain Driven Design — GitHub
ddd-by-examples / library
A comprehensive Domain-Driven Design example with problem space strategic analysis and various tactical patterns.
The components of a rich model such as:
- high cohesion
- value objects
- bounded contexts
- events
- hexagonal architecture
- example of usage
- and of course a pragmatic approach
will be presented in the following articles. Just bypassing the anemic model will allow you to significantly improve the quality of your work. Stay close to Domain Driven Design.
Top comments (0)