One of the benefit of rich domain classes is you can call their behaviour (methods) everytime you have the reference to the object in any layer. Also, you tend to write small and distributed methods that collaborate together. In anemic domain classes, you tend to write fat procedural methods (in service layer) that are usually driven by use case. They are usually less maintainable compared to rich domain classes.
An example of domain classes with behaviours:
class Order {
String number
List<OrderItem> items
ItemList bonus
Delivery delivery
void addItem(Item item) { // add bonus if necessary }
ItemList needToDeliver() { // items + bonus }
void deliver() {
delivery = new Delivery()
delivery.items = needToDeliver()
}
}
Method needToDeliver()
will return list of items that need to be delivered including bonus. It can be called inside the class, from another related class, or from another layer. For example, if you pass Order
to view, then you can use needToDeliver()
of selected Order
to display list of items to be confirmed by user before they click on save button to persist the Order
.
Responding To Comment
This is how I use the domain class from controller:
def save = {
Order order = new Order()
order.addItem(new Item())
order.addItem(new Item())
repository.create(order)
}
The creation of Order
and its LineItem
is in one transaction. If one of the LineItem
can't be created, no Order
will be created.
I tend to have method that represent a single transaction, such as:
def deliver = {
Order order = repository.findOrderByNumber('ORDER-1')
order.deliver()
// save order if necessary
}
Anything inside deliver()
will be executed as one single transaction. If I need to execute many unrelated methods in a single transaction, I would create a service class.
To avoid lazy loading exception, I use JPA 2.1 named entity graph. For example, in controller for delivery screen, I can create method to load delivery
attribute and ignore bonus
, such as repository.findOrderByNumberFetchDelivery()
. In bonus screen, I call another method that load bonus
attribute and ignore delivery
, such as repository.findOrderByNumberFetchBonus()
. This requires dicipline since I still can't call deliver()
inside bonus screen.