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

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

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


person Adam Ness    schedule 22.10.2008    source источник


Ответы (4)


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

Вы демонстрируете большую проницательность, отмечая противоречие между выразительной моделью предметной области и разделением интересов. Это очень похоже на напряжение в вопросе, который я задал о Говори, не спрашивай, и принцип единой ответственности.

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

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

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

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

Но учтите, что если у вас нет логики домена, вы получите анемичные объекты домена, то есть мешки безмозглых геттеров и сеттеров, вот как некоторые магазины заявляют, что создают уровни 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 слишком много раскрывает то, как реализована ваша сущность, и эти средства доступа отвлекают от логики предметной области. Я думаю, что единственный класс, который должен иметь методы получения и набора, - это класс, имя которого заканчивается на «Аксессор».

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

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

person moffdub    schedule 23.10.2008
comment
Короче вы предлагаете доступ к репозиторию из домена? - person aaimnr; 10.12.2009

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

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

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

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

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

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

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

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

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

person S.Lott    schedule 22.10.2008

Я согласен с 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);

}

Видите разницу в наследовании? Я делаю :)

person Steve    schedule 11.12.2009

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

person Manu    schedule 22.10.2008
comment
Это не отвечает на вопрос. Вопрос в том, нужно ли внедрять репозиторий в домен? Если так, это нарушит разделение проблем. С другой стороны, если эти действия будут выполняться службой, это может привести к модели анемичного домена. - person aaimnr; 10.12.2009