Одним из преимуществ богатых доменных классов является то, что вы можете вызывать их поведение (методы) каждый раз, когда у вас есть ссылка на объект на любом уровне. Кроме того, вы склонны писать небольшие распределенные методы, которые взаимодействуют друг с другом. В анемичных доменных классах вы, как правило, пишете сложные процедурные методы (на уровне обслуживания), которые обычно определяются вариантом использования. Обычно они менее удобны в обслуживании по сравнению с богатыми предметными классами.
Пример классов предметной области с поведением:
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()на бонусный экран.