DDD: первичные ключи (идентификаторы) и ORM (например, NHibernate) - PullRequest
16 голосов
/ 14 мая 2009

Почему считается допустимым иметь поле Id в доменных объектах? Я видел несколько решений, которые предоставляют базовый класс с Id и GetHashCode / Equals на основе Id.

Мое понимание доменной модели состоит в том, что она должна содержать только вещи, связанные с доменом. Хотя в редких случаях (отслеживаемые порядки) идентификаторы имеют смысл, в большинстве случаев они не предоставляют ничего, кроме простого способа ссылки на объекты в DB / on UI.

Я также не вижу преимуществ Equals / GetHashCode, поскольку реализация Identity Map должна гарантировать, что ссылочное равенство равно равенству идентификатора в любом случае.

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

UPDATE:

Спасибо за ответы.

Некоторые из них предполагают, что наличие идентификатора является единственным способом для ORM выполнить обновление БД. Я не думаю, что это так. ORM уже отслеживает все сущности, загруженные из БД, поэтому он может легко получить внутренний идентификатор, когда он ему нужен.

ОБНОВЛЕНИЕ 2:

Ответ на правосудие и аналогичные пункты: Что если у нас есть веб-приложение и нужен способ ссылки на сущность между сеансами? Как редактировать / ресурс / идентификатор?

Ну, я вижу это как особую потребность в ограниченном пользовательском интерфейсе / среде, а не как модель предметной области. Для этого сценария достаточно наличия службы приложений или хранилища с методом GetIdentitity (в соответствии с методом Load (identity)).

Ответы [ 8 ]

4 голосов
/ 14 мая 2009

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

Типичные сценарии:

  • автоинкрементное значение, сгенерированное базой данных
  • guid, сгенерированный NHibernate
  • guid, сгенерированный бизнес-логикой

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

Сравнение на основе идентификатора - дело вкуса. Лично мне нравится простой и надежный код, и сравнение на основе идентификатора является простым и надежным, в то время как глубокое сравнение всегда немного рискованно, подвержено ошибкам, не поддерживается. Таким образом, вы в конечном итоге получите оператор ==, сравнивающий идентичность в памяти, и оператор Equals, сравнивающий идентичность для бизнеса (и базы данных).

NHibernate - это менее навязчивый способ сопоставления модели классов с реляционной базой данных, которую я знаю. В нашем большом проекте у нас есть первичные ключи и свойства версии (они используются для оптимистической блокировки). Можно утверждать, что это навязчиво, потому что оно не используется для бизнес-логики.

NH не требует наличия этих свойств. (однако ему требуется одно из свойств в качестве первичного ключа.) Но учтите:

  • Просто работает лучше, например. пессимистическая блокировка возможна только с соответствующим свойством,
  • быстрее: int id работает лучше во многих базах данных, чем другие типы данных
  • и проще: брать свойства со смыслом для бизнеса не рекомендуется по нескольким причинам.

Так зачем делать вашу жизнь сложнее, чем необходимо?


Редактировать: Просто прочитайте документацию и обнаружили, что NHibernate не нуждается в свойстве id в постоянном классе! Если у вас нет идентификатора, , вы не можете использовать некоторые функции . Рекомендуется иметь, это просто делает вашу жизнь проще.

3 голосов
/ 14 мая 2009

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

Примером, конечно, является пара URL-адресов / resource / list и / resource / show / id (соответствующая соответствующей паре действий контроллера в asp.net mvc). Переходя от одного к другому, есть два совершенно независимых сеанса, но они должны общаться (через HTTP и браузер) друг с другом.

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

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

1 голос
/ 14 мая 2009

В Nh нельзя использовать равенство ссылок для отдельных экземпляров. Это мешает вам использовать NH в клиентах, которые хотели бы использовать это поведение (на мой взгляд, это правильное поведение, NH не зависит от магии!).

В веб-приложениях или сессиях, основанных на сеансах, где вы можете перейти к БД в рамках одной сессии ISession, я думаю, что я прав, говоря, что вы можете положиться на равенство ссылок. Но в сценарии с умным клиентом или, более конкретно, в любом сценарии, в котором две сессии ISessions совместно используют экземпляр (один загружается из или вставляется в базу данных, а затем передается во второй), вам необходимо, чтобы поля (поля) идентификатора базы данных сохранялись вместе с экземпляр.

второй сеанс, приведенный ниже, не предполагал бы, что передаваемый экземпляр уже вставлен и требует обновления, и даже если вы изменили его на обновление, я предполагаю, что я прав, думая, что он не собирается знать идентификатор БД класса.

class SomeEntityWithNoId
{
public  blah etc {}
}

class dao
{

void DateMethod()
{
var s1 = sessionFactory.OpenSession();
var instance = new SomeEntityWithNoId();
instance.Blah = "First time, you need to insert";
s1.Save(s1); //now instance is persisted, has a DB ID such as identity or Hilo or whatever
s1.close();
var s2 = sessionFactory.OpenSession();
s2.Blah = "Now update, going to find that pretty hard as the 2nd session won't know what ID to use for the UPDATE statement";

s2.Update(instance); //results in boom! exception, the second session has no idea *how* to update this instance.




}
}

Если вас беспокоят поля (поля) идентификаторов, которые, по вашему мнению, являются правильными, в значительной степени бесполезны для чего-либо, кроме проблем постоянства, вы можете сделать их частными, поэтому по крайней мере это поведение и семантика будут инкапсулированы. Вы можете обнаружить, что этот подход вносит некоторые трения в TDD.

Я думаю, вы правы относительно карт ID, по крайней мере, в отношении NH. Если экземпляр является временным, когда он впервые присоединяется к сеансу, вашему типу не нужно хранить его идентификатор базы данных в поле. Я думаю, что сессия будет знать, как управлять своими отношениями с БД. Я уверен, что вы можете провести тест, чтобы доказать это поведение; p

0 голосов
/ 14 мая 2009

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

Узнайте о них здесь

0 голосов
/ 14 мая 2009

Обычно я включаю Id в домен в качестве базового класса Entity. Я не знаю другого способа использовать ORM.

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

0 голосов
/ 14 мая 2009

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

Вы можете использовать естественную идентификацию, но это подвержено ошибкам.

Просто переименуйте Id в Identity. Это выглядит лучше в доменной модели.

Сущности называются сущностями, потому что они имеют идентичность.

0 голосов
/ 14 мая 2009

при условии, что ваш идентификатор является вашим первичным ключом, вы не сможете сохранить данные в БД без него.
Что касается equals и GetHashCode, они предназначены для упорядочивания в списках, где вы создаете заказное упорядочение.

Спасибо за ответы.

Некоторые из них предполагают, что наличие идентификатора - единственный способ для ORM выполнить обновление БД. Я не думаю, что это так. ORM уже отслеживает все объекты, загруженные из БД, поэтому он может легко получить внутренний идентификатор, когда он ему нужен.

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

0 голосов
/ 14 мая 2009

Вы должны иметь его так или иначе, чтобы ORM мог вернуться к базе данных для выполнения обновлений; Я думаю, что это может быть частным и скрытым в базовом классе, но это как бы нарушает концепцию POCO.

...