I’ll start this article with this sample of code and ask you: Do you think that the code below is a good domain model ?
Obs.: It is totally fine to have this type of class when you don’t have business complexity, but you should keep in mind that this is not a domain model. It is more likely to be a data transfer object (DTO).
It is being a while that I use to see “domain models” implemented this way and all the logic is put apart on a such called “SomethingService”, in this case see the OrderService sample:
Both examples, show in practice what we call as anemic domain model, very well argued by Martin Fowler. Also according to him, a domain model is an object model of the domain that incorporates both behavior and data.
If we refactor the code, and put the behavior and data on the same class, the Order domain model becomes clearer and shows its purpose:
Ok, looks good so far.
But now, what would happen if we call the constructor of this class and try to add an item to this order ?
BOOM! Null Reference Exception, because our domain model still lacks of state management and the “Items” collection is null.
The object should encapsulate the way it is built, changed and keep itself in a valid state.
Lets refactor our domain model with private setters and constructors avoiding external code to build/change it to an invalid state.
Now we need a way to create this order in a valid state. In order to do that, we need a method able to create a valid order:
Let’s do the same for the OrderItem:
Now if we try to consume the Order class, the only option that we have to create it, is by using the static method called “New”. If we try to change the state, it will not be possible anymore, because its properties are read-only from any external code.
You can notice that our Order class has a property that stores a list of order items and also a method called AddItem(), which contains the logic needed for adding a valid order item. But external agents are able to access the property and add the item directly without any validation, since the List offered by the framework is mutable.
One approach to tackle this issue is always exposing an immutable copy of these items and encapsulating the original source:
Now your entity modifies this list and external code can see the collection but not modify it.
The final result is a class fully independent, incorruptible and auto manageable.
To see the final solution, please access this gist.
This article is also available in Portuguese.