Подтвердить что ты не робот

Как избежать аномальных моделей доменов и поддерживать разделение проблем?

Кажется, что решение сделать ваши объекты полностью осведомленными о своих ролях внутри системы и все же избежать слишком большого количества зависимостей в модели домена в базе данных и слоях обслуживания?

Например: скажите, что у меня есть объект с историей ревизий и несколько "таблиц поиска", которые ссылаются на данные, ваш объект объекта должен иметь методы для получения сведений из некоторых таблиц поиска, будь то путем предоставления доступ к строкам таблицы поиска или делегирование им методов, но для этого он зависит от уровня базы данных для чтения данных из этих строк. Кроме того, когда объект сохраняется, он должен знать не только о том, как сохранить себя, но и сохранить записи в истории изменений. Нужно ли передавать ссылки на десятки разных объектов уровня данных и объектов обслуживания на объект модели? Это похоже на то, что логика намного сложнее понять, чем просто передавать тонкие модели для обслуживания объектов уровня, но я слышал, что многие "мудрые люди" рекомендуют такую ​​структуру.

4b9b3361

Ответ 1

Действительно хороший вопрос. Я потратил немало времени на размышления о таких темах.

Вы демонстрируете большую проницательность, отмечая напряженность между моделью экспрессивных доменов и разделением проблем. Это очень похоже на напряженность в вопросе, который я задал о Tell Do not Ask and Single Responsibility Principle.

Вот мой взгляд на эту тему.

Модель домена анемична, поскольку она не содержит логики домена. Другие объекты получают и устанавливают данные с использованием объекта анемичного домена. То, что вы описываете, для меня не похоже на логику домена. Это может быть, но, как правило, справочные таблицы и другой технический язык, скорее всего, означают что-то для нас, но не обязательно что-то для клиентов. Если это неверно, уточните.

В любом случае, построение и сохранение объектов домена не должны содержаться в самих объектах домена, потому что это не логика домена.

Итак, чтобы ответить на вопрос, нет, вы не должны вводить целую кучу не-доменных объектов/понятий, таких как таблицы поиска и другие детали инфраструктуры. Это утечка одной проблемы в другую. Шаблоны Factory и репозитория из проекта, управляемого доменом, лучше всего подходят для устранения этих проблем, помимо самой модели домена.

Но обратите внимание, что если у вас нет какой-либо логики домена, то в конечном итоге у вас появятся объекты анемичного домена, т.е. сумки безмозглых геттеров и сеттеров, а именно некоторые магазины утверждают, что SOA/сервисные уровни.

Итак, как вы получаете лучшее из обоих миров? Как вы фокусируете объекты домена только в логике домена, сохраняя при этом UI, конструкцию, настойчивость и т.д.? Я рекомендую использовать такую ​​технику, как Double Dispatch, или в какой-то форме доступ к ограниченному доступу.

Вот пример Double Dispatch. Скажем, у вас есть эта строка кода:

entity.saveIn(repository);

В вашем вопросе saveIn() будет иметь всевозможные знания о слое данных. Используя Double Dispatch, saveIn() делает следующее:

repository.saveEntity(this.foo, this.bar, this.baz);

И метод saveEntity() репозитория имеет все знания о том, как сохранить в слое данных, как и должно быть.

В дополнение к этой настройке вы можете:

repository.save(entity);

который просто вызывает

entity.saveIn(this);

Я перечитываю это, и я замечаю, что объект все еще тонкий, потому что он просто отправляет его сохранение в репозиторий. Но в этом случае объект должен быть тонким, потому что вы не описали какую-либо другую логику домена. В этой ситуации вы могли бы сказать "винт Double Dispatch, дать мне аксессуры".

И да, вы могли бы, но IMO в нем слишком много показывает, как реализуется ваша сущность, и эти аксессоры являются отвлекающими факторами от логики домена. Я думаю, что единственным классом, который должен иметь get и sets, является класс, чье имя заканчивается в "Accessor".

Я скоро завершу это. Лично я не записываю свои сущности с помощью методов saveIn(), потому что я думаю, что даже с помощью метода saveIn() вы можете помешать объекту домена отвлекаться. Я использую либо шаблон класса друга, либо пакетный доступ, либо, возможно, шаблон Builder.

Хорошо, все готово. Как я уже сказал, я немного одержим этой темой.

Ответ 2

"тонкие модели для объектов уровня сервиса" - это то, что вы делаете, когда действительно хотите написать уровень сервиса.

ORM - это то, что вы делаете, когда не хотите писать сервисный уровень.

Когда вы работаете с ORM, вы по-прежнему осознаете тот факт, что навигация может включать запрос, но вы не останавливаетесь на нем.

Таблицы поиска могут быть реляционным костылем, который используется, когда не существует полной модели объекта. Вместо того, чтобы ссылаться на вещи, у вас есть коды, которые нужно искать. Во многих случаях коды передаются не более, чем статический пул строк с ключами базы данных. И соответствующие методы попадают в нечетные места в программном обеспечении.

Однако, если существует более полная объектная модель, у нас есть первоклассные вещи вместо этих вырожденных значений поиска.

Например, у меня есть некоторые бизнес-транзакции, которые имеют один из n разных "тарифных планов" - своего рода модель ценообразования. Прямо сейчас у старой реляционной базы данных есть тарифный план в виде таблицы поиска с кодом, некоторыми номерами цен и (иногда) описанием.

[Все знают коды - коды священны. Никто не уверен, какими должны быть правильные описания. Но они знают коды.]

Но действительно, "тарифный план" - это объект, связанный с контрактом; тарифный план имеет метод, который вычисляет конечную цену. Когда приложение запрашивает контракт по цене, контракт делегирует часть ценовой работы соответствующему объекту тарифного плана.

Возможно, был некоторый запрос к базе данных для поиска тарифного плана при создании контрактной цены, но это было связано с делегированием ответственности между двумя классами.

Ответ 3

Я сражаюсь с DeadBeef - в этом есть напряженность. Я действительно не вижу, как модель домена "анемия" просто потому, что она не спасает себя.

Там должно быть гораздо больше. то есть. Это анемия, потому что служба выполняет все бизнес-правила, а не объект домена.

Service(IRepository) injected

Save(){

DomainEntity.DoSomething();
Repository.Save(DomainEntity);

}

'Do Something' is the business logic of the domain entity.

**This would be anemic**:
Service(IRepository) injected

Save(){

if(DomainEntity.IsSomething)
  DomainEntity.SetItProperty();
Repository.Save(DomainEntity);

}

См. разницу в наследовании? Я делаю:)

Ответ 4

Попробуйте "шаблон хранилища" и "Проект, управляемый доменом". DDD предлагает определить определенные объекты как совокупные корни других объектов. Каждый агрегат инкапсулирован. Сущности являются "упорством невежественным". Весь код, связанный с сохранением, помещается в объект репозитория, который управляет доступом к данным для объекта. Таким образом, вам не нужно смешивать код, связанный с сохранением, с вашей бизнес-логикой. Если вас интересует DDD, просмотрите книгу eric evans.