Я решаю, следует ли мне использовать модель расширенного домена вместо модели анемического домена, и ищу хорошие примеры того и другого.
Я создавал веб-приложения с использованием модели анемичного домена, поддерживаемой системой уровня Service -> Repository -> Storage , используя FluentValidation для проверки BL и помещая все мои BL на уровень Service.
Я прочитал книгу DDD Эрика Эвана, и он (вместе с Фаулером и другими), похоже, считает, что модели анемической области - это антипаттерн.
Так что мне просто очень хотелось разобраться в этой проблеме.
Кроме того, я действительно ищу несколько хороших (базовых) примеров модели расширенной области и преимуществ по сравнению с моделью анемической области, которую она предоставляет.
Ответы:
Разница в том, что анемичная модель отделяет логику от данных. Логика часто помещаются в классах по имени
**Service
,**Util
,**Manager
,**Helper
и так далее. Эти классы реализуют логику интерпретации данных и поэтому принимают модель данных в качестве аргумента. Напримерв то время как подход с расширенной областью данных меняет это положение, помещая логику интерпретации данных в модель расширенной области. Таким образом, он объединяет логику и данные, и полноценная модель предметной области будет выглядеть так:
Это имеет большое влияние на целостность объекта. Поскольку логика интерпретации данных обертывает данные (доступ к данным возможен только через методы объекта), методы могут реагировать на изменения состояния других данных -> Это то, что мы называем поведением.
В анемичной модели модели данных не могут гарантировать, что они находятся в законном состоянии, в то время как в модели с расширенной предметной областью это возможно. Богатая модель предметной области применяет принципы объектно-ориентированного программирования, такие как инкапсуляция, скрытие информации и объединение данных и логики, и поэтому анемичная модель - это антипаттерн с точки зрения объектно-ориентированного подхода.
Для более глубокого понимания загляните в мой блог https://www.link-intersystems.com/blog/2011/10/01/anemic-vs-rich-domain-models/
источник
В этом сообщении в блоге Божидар Божанов, кажется, выступает в пользу анемичной модели .
Вот краткое изложение, которое он представляет:
объекты домена не должны управляться Spring (IoC), в них не должно быть внедренных DAO или чего-либо, связанного с инфраструктурой
объекты домена имеют объекты домена, от которых они зависят, установленные спящим режимом (или механизмом сохранения)
объекты домена выполняют бизнес-логику, как и основная идея DDD, но это не включает запросы к базе данных или CRUD - только операции над внутренним состоянием объекта
DTO нужны редко - объекты домена в большинстве случаев сами являются DTO (что позволяет сэкономить некоторый шаблонный код)
службы выполняют операции CRUD, отправляют электронные письма, координируют объекты домена, генерируют отчеты на основе нескольких объектов домена, выполняют запросы и т. д.
уровень сервиса (приложения) не такой тонкий, но не включает бизнес-правила, присущие объектам домена
следует избегать генерации кода. Абстракция, шаблоны проектирования и DI должны использоваться для преодоления необходимости генерации кода и, в конечном итоге, для избавления от дублирования кода.
ОБНОВИТЬ
Недавно я прочитал эту статью, в которой автор выступает за своего рода гибридный подход - объекты предметной области могут отвечать на различные вопросы, основываясь исключительно на своем состоянии (что в случае полностью анемичных моделей, вероятно, будет сделано на уровне обслуживания)
источник
Моя точка зрения такова:
Модель анемичного домена = таблицы базы данных, сопоставленные с объектами (только значения полей, без реального поведения)
Богатая модель предметной области = набор объектов, демонстрирующих поведение
Если вы хотите создать простое приложение CRUD, возможно, достаточно анемичной модели с классической платформой MVC. Но если вы хотите реализовать какую-то логику, анемичная модель означает, что вы не будете заниматься объектно-ориентированным программированием.
* Обратите внимание, что поведение объекта не имеет ничего общего с настойчивостью. Другой уровень (Data Mappers, Repositories и т.д.) отвечает за сохранение объектов домена.
источник
x
,y
,sum
иdifference
. Это четыре вещи. Или вы можете возразить, что это сложение и вычитание (две вещи). Или вы можете возразить, что это математика (одно). В блогах есть много сообщений о том, как найти баланс при применении SRP. Вот один: hackernoon.com/…На рисунке 1 показана модель анемической области, которая в основном представляет собой схему с геттерами и сеттерами.
В этой более богатой модели, вместо того, чтобы просто открывать свойства для чтения и записи, публичная поверхность Customer состоит из явных методов.
источник
Address
, аExtendedAddress
унаследованный отAddress
, с несколькими дополнительными свойствами? 2) Или изменитьCustomerCreditCard
параметры конструктора, чтобы взятьBankID
вместоBankName
?Одним из преимуществ богатых доменных классов является то, что вы можете вызывать их поведение (методы) каждый раз, когда у вас есть ссылка на объект на любом уровне. Кроме того, вы склонны писать небольшие распределенные методы, которые взаимодействуют друг с другом. В анемичных доменных классах вы, как правило, пишете сложные процедурные методы (на уровне обслуживания), которые обычно определяются вариантом использования. Обычно они менее удобны в обслуживании по сравнению с богатыми предметными классами.
Пример классов предметной области с поведением:
Метод
needToDeliver()
вернет список предметов, которые необходимо доставить, включая бонус. Его можно вызвать внутри класса, из другого связанного класса или из другого уровня. Например, если вы перейдетеOrder
к просмотру, то вы можете использоватьneedToDeliver()
selectedOrder
для отображения списка элементов, которые должны быть подтверждены пользователем, прежде чем они нажмут кнопку сохранения, чтобы сохранитьOrder
.Ответ на комментарий
Вот как я использую класс домена из контроллера:
Создание
Order
иLineItem
это в одной транзакции. Если один из нихLineItem
не может быть создан, неOrder
будет создан.Я предпочитаю использовать метод, представляющий одну транзакцию, например:
Что-нибудь внутри
deliver()
будет выполнено как одна транзакция. Если мне нужно выполнить много несвязанных методов в одной транзакции, я бы создал класс обслуживания.Чтобы избежать исключения из ленивой загрузки, я использую именованный граф сущностей JPA 2.1. Например, в контроллере экрана доставки я могу создать метод для загрузки
delivery
атрибута и игнорированияbonus
, напримерrepository.findOrderByNumberFetchDelivery()
. На бонусном экране я вызываю другой метод, который загружаетbonus
атрибут и игнорирует егоdelivery
, напримерrepository.findOrderByNumberFetchBonus()
. Это требует дисциплины, так как я все еще не могу позвонитьdeliver()
на бонусный экран.источник
Когда я писал монолитные настольные приложения, я создавал богатые модели предметной области, и мне нравилось их создавать.
Теперь я пишу крошечные микросервисы HTTP, в них как можно меньше кода, включая анемичные DTO.
Я думаю, что DDD и этот анемичный аргумент относятся к эпохе монолитных настольных или серверных приложений. Я помню ту эпоху и согласен, что анемичные модели - это странно. Я построил большое монолитное приложение для торговли на Форексе, и на самом деле не было модели, это было ужасно.
В случае микросервисов небольшие сервисы с их разнообразным поведением, возможно, представляют собой составляемые модели и агрегаты внутри домена. Таким образом, сами реализации микросервисов могут не потребовать дополнительных DDD. Приложение микросервиса может быть доменом.
Микросервис заказов может иметь очень мало функций, выраженных как ресурсы RESTful, через SOAP или что-то еще. Код микросервиса заказов может быть предельно простым.
Более крупный, более монолитный одиночный (микро) сервис, особенно тот, который хранит его модель в ОЗУ, может выиграть от DDD.
источник
Я думаю, что корень проблемы в ложной дихотомии. Как можно выделить эти две модели: богатую и «анемичную» и противопоставить их друг другу? Я думаю, что это возможно, только если у вас неправильное представление о том, что такое класс . Не уверен, но мне кажется, что я нашел это в одном из видео Божидара Божанова на Youtube. Класс - это не данные + методы над этими данными. Это совершенно неверное понимание, которое приводит к разделению классов на две категории: только данные, поэтому анемичная модель и данные + методы - настолько богатая модель (вернее, есть третья категория: даже только методы).
Верно то, что класс - это понятие в какой-то онтологической модели, слово, определение, термин, идея, это ОБОЗНАЧЕНИЕ. . И это понимание устраняет ложную дихотомию: у вас не может быть ТОЛЬКО анемичной модели или ТОЛЬКО богатой модели, потому что это означает, что ваша модель неадекватна, она не имеет отношения к реальности: некоторые концепции имеют только данные, некоторые из них имеют только методы, некоторые из них смешанные. Поскольку в данном случае мы пытаемся описать некоторые категории, наборы объектов, отношения, концепции с помощью классов, и, как мы знаем, некоторые концепции являются только процессами (методами), некоторые из них являются только наборами атрибутов (данных), некоторые из них это отношения с атрибутами (смешанные).
Я считаю, что адекватное приложение должно включать все виды классов и избегать фанатичного самоограничения только одной моделью. Независимо от того, как логика представляет: с кодом или с интерпретируемыми объектами данных (например, Free Monads ), в любом случае: у нас должны быть классы (концепции, денотаты), представляющие процессы, логику, отношения, атрибуты, функции, данные и т. Д., А не попытаться избежать некоторых из них или свести их всех только к одному виду.
Итак, мы можем извлечь логику в другой класс и оставить данные в исходном, но это не имеет смысла, потому что некоторая концепция может включать атрибуты и отношения / процессы / методы, и их разделение будет дублировать концепцию под двумя именами, которые могут быть сводится к шаблонам: «ОБЪЕКТ-Атрибуты» и «ОБЪЕКТ-Логика». Это нормально для процедурных и функциональных языков из-за их ограничений. но это чрезмерное самоограничение для языка, который позволяет вам описывать все виды концепций.
источник
Анемичные доменные модели важны для ORM и простой передачи по сетям (жизненная сила всех коммерческих приложений), но объектно-ориентированный подход очень важен для инкапсуляции и упрощения «транзакционных / обрабатывающих» частей вашего кода.
Поэтому важно уметь идентифицировать и преобразовывать из одного мира в другой.
Назовите модели Anemic как-то вроде AnemicUser или UserDAO и т. Д., Чтобы разработчики знали, что есть лучший класс для использования, а затем создайте соответствующий конструктор для класса none Anemic.
и метод адаптера для создания анемичного класса для транспортировки / сохранения
Стремитесь использовать неанемичного пользователя везде, кроме транспорта / постоянства
источник
Вот пример, который может помочь:
Анемичный
Без анемии
источник
Классический подход к DDD не требует любой ценой избегать использования анемичных и богатых моделей. Однако MDA может по-прежнему применять все концепции DDD (ограниченные контексты, контекстные карты, объекты значений и т. Д.), Но во всех случаях использовать модели Anemic vs Rich. Во многих случаях использование доменных служб для оркестровки сложных вариантов использования домена в наборе агрегатов домена является гораздо лучшим подходом, чем простой вызов агрегатов из уровня приложения. Единственное отличие от классического подхода DDD в том, где находятся все проверки и бизнес-правила? Есть новая конструкция, известная как валидаторы моделей. Валидаторы гарантируют целостность полной входной модели до того, как будет реализован какой-либо вариант использования или рабочий процесс домена. Совокупные корневые и дочерние сущности анемичны, но каждый может иметь свои собственные валидаторы модели, вызываемые при необходимости, его корневым валидатором. Валидаторы по-прежнему придерживаются SRP, просты в обслуживании и поддаются модульному тестированию.
Причина этого сдвига в том, что сейчас мы больше двигаемся к подходу к микросервисам сначала API, а не UX. REST сыграл в этом очень важную роль. Традиционный подход к API (из-за SOAP) изначально был основан на командном API и HTTP-глаголах (POST, PUT, PATCH, GET и DELETE). API на основе команд хорошо сочетается с объектно-ориентированным подходом Rich Model и все еще очень актуален. Однако простые API-интерфейсы на основе CRUD, хотя они могут вписаться в расширенную модель, гораздо лучше подходят для простых анемичных моделей, валидаторов и доменных служб для организации всего остального.
Мне нравится DDD во всем, что он может предложить, но приходит время, когда вам нужно немного расширить его, чтобы он соответствовал постоянно меняющимся и лучшему подходу к архитектуре.
источник