Одним из преимуществ богатых доменных классов является то, что вы можете вызывать их поведение (методы) каждый раз, когда у вас есть ссылка на объект на любом уровне. Кроме того, вы склонны писать небольшие распределенные методы, которые взаимодействуют друг с другом. В анемичных доменных классах вы, как правило, пишете сложные процедурные методы (на уровне обслуживания), которые обычно определяются вариантом использования. Обычно они менее удобны в обслуживании по сравнению с богатыми предметными классами.
Пример классов предметной области с поведением:
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()
}
}
Метод needToDeliver()
вернет список предметов, которые необходимо доставить, включая бонус. Его можно вызвать внутри класса, из другого связанного класса или из другого уровня. Например, если вы перейдете Order
к просмотру, то вы можете использовать needToDeliver()
selected Order
для отображения списка элементов, которые должны быть подтверждены пользователем, прежде чем они нажмут кнопку сохранения, чтобы сохранитьOrder
.
Ответ на комментарий
Вот как я использую класс домена из контроллера:
def save = {
Order order = new Order()
order.addItem(new Item())
order.addItem(new Item())
repository.create(order)
}
Создание Order
и LineItem
это в одной транзакции. Если один из них LineItem
не может быть создан, не Order
будет создан.
Я предпочитаю использовать метод, представляющий одну транзакцию, например:
def deliver = {
Order order = repository.findOrderByNumber('ORDER-1')
order.deliver()
// save order if necessary
}
Что-нибудь внутри deliver()
будет выполнено как одна транзакция. Если мне нужно выполнить много несвязанных методов в одной транзакции, я бы создал класс обслуживания.
Чтобы избежать исключения из ленивой загрузки, я использую именованный граф сущностей JPA 2.1. Например, в контроллере экрана доставки я могу создать метод для загрузкиdelivery
атрибута и игнорирования bonus
, например repository.findOrderByNumberFetchDelivery()
. На бонусном экране я вызываю другой метод, который загружает bonus
атрибут и игнорирует его delivery
, например repository.findOrderByNumberFetchBonus()
. Это требует дисциплины, так как я все еще не могу позвонить deliver()
на бонусный экран.