Вопрос о репозиториях и методах их сохранения для доменных объектов - PullRequest
9 голосов
/ 23 марта 2009

У меня есть несколько нелепый вопрос относительно DDD, шаблонов репозитория и ORM. В этом примере у меня есть 3 класса: Адрес , Компания и Персона . Лицо является членом Компании и имеет адрес. Компания также имеет адрес.

Эти классы отражают модель базы данных. Я удалил все зависимости своих Моделей, чтобы они не были привязаны к конкретной библиотеке ORM, такой как NHibernate или LinqToSql. Эти зависимости рассматриваются внутри хранилищ.

Внутри одного из Хранилищ есть SavePerson (Person person) метод, который вставляет / обновляет Person, в зависимости от того, существует ли он уже в базе данных.

Поскольку объект Person имеет Company, в настоящее время я сохраняю / обновляю значения свойства Company также при выполнении этого вызова SavePerson. Я вставляю / обновляю все данные Компании - Имя и Адрес - во время этой процедуры.

Однако мне действительно трудно подумать о случае, когда данные Компании могут измениться при работе с Лицом - я хочу только иметь возможность назначить Компанию Лицу или перевести Лицо в другую Компанию. Я не думаю, что когда-либо хочу создать новую компанию вместе с новым человеком. Поэтому вызовы SaveCompany вводят ненужные вызовы базы данных. При сохранении персонажа я должен просто обновить столбец CompanyId.

Но поскольку у класса Person есть свойство Company, я несколько склонен обновлять / вставлять его вместе с ним. С точки зрения строгости и чистоты метод SavePerson должен сохранять весь объект Person.

Каким будет предпочтительный путь? Просто вставляете / обновляете CompanyId свойства Company при сохранении Person или сохранении всех его данных? Или вы бы создали два разных метода для обоих сценариев (как бы вы их назвали?)

Кроме того, еще один вопрос, у меня в настоящее время есть разные методы для сохранения Персоны, Адреса и Компании, поэтому, когда я сохраняю Компанию, я также вызываю SaveAddress. Давайте предположим, что я использую LinqToSql - это означает, что я не вставляю / не обновляю компанию и адрес в одном и том же запросе Linq. Я предполагаю, что есть 2 Выборочных вызова (проверка, существует ли компания, проверка, существует ли адрес). А затем два вызова вставки / обновления для обоих. Еще больше, если вводится больше классов составной модели. Есть ли способ для LinqToSql оптимизировать эти вызовы?

public class Address
{
    public int AddressId { get; set; }
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }        
}

public class Company
{
    public int CompanyId { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}


public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public Company Company { get; set; }
    public Address Address { get; set; }

}

Редактировать

Также см. следующий вопрос . Как объекты-значения хранятся в базе данных?

Ответы [ 2 ]

6 голосов
/ 24 марта 2009

В последнее время я сам использовал подход IRepository, который предлагает Кит. Но вы не должны фокусироваться на этом паттерне здесь. Вместо этого в пьесе DDD есть еще несколько пьес, которые можно применить здесь.

Используйте Значения объектов для ваших адресов

Во-первых, есть концепция объектов стоимости (VO), которую вы можете применить здесь. В вашем случае это будет адрес. Разница между объектом-значением и объектом-сущностью заключается в том, что сущности имеют идентичность; ВО не делают. Идентификация ВО на самом деле является суммой его свойств, а не уникальной идентичностью. В книге «Проектирование доменных дисков быстро» (это также бесплатная загрузка в формате PDF) он очень хорошо объясняет это, заявляя, что адрес на самом деле является просто точкой на Земле и не требует отдельной идентичности типа SocialSecurity как человек. Эта точка на Земле является комбинацией улицы, числа, города, почтового индекса и страны. Он может иметь значения широты и долготы, но по определению это даже VO, потому что это комбинация из двух точек.

Используйте Услуги для объединения ваших сущностей в единую сущность, с которой вы будете действовать.

Кроме того, не забывайте о концепции служб в пьесе DDD. В вашем примере этот сервис будет:

public class PersonCompanyService
{
  void SavePersonCompany(IPersonCompany personCompany)
  {
    personRepository.SavePerson();
    // do some work for a new company, etc.
    companyRepository.SaveCompany();
  }
}

Существует потребность в услуге, когда у вас есть две сущности, которым обеим нужно одинаковое действие для координации комбинации других действий. В вашем случае сохранение Person () и одновременное создание пустой Company ().

ORMs обычно требуется личность, точка.

Теперь, как бы вы сохранили адрес VO в базе данных? Очевидно, вы бы использовали IAddressRepository. Но так как большинство ORM (т.е. LingToSql) требуют, чтобы все объекты имели идентичность, вот хитрость: пометьте идентичность как внутреннюю в вашей модели, чтобы она не отображалась за пределами слоя модели. Это собственный совет Стивена Сандерсона.

public class Address
{
  // make your identity internal
  [Column(IsPrimaryKey = true
    , IsDbGenerated = true
    , AutoSync = AutoSync.OnInsert)]
  internal int AddressID { get; set; }

  // everything else public
  [Column]
  public string StreetNumber { get; set; }
  [Column]
  public string Street { get; set; }
  [Column]
  public string City { get; set; }
  ...
}
0 голосов
/ 23 марта 2009

Из моего недавнего опыта использования шаблона репозитория, я думаю, вы бы выиграли от использования универсального репозитория, теперь общего IRepository of T. Таким образом, вам не нужно будет добавлять методы репозитория, такие как SavePerson (Person person). Вместо этого у вас будет что-то вроде:

IRepository<Person> personRepository = new Repository<Person>();
Person realPerson = new Person();
personRepository.SaveOrUpdate(realPerson);

Этот метод также пригоден для разработки через тестирование и макетирование.

Я чувствую, что вопросы о поведении в вашем описании будут касаться Домена, возможно, вам следует добавить метод AddCompany в свой класс Person и изменить свойство Company на

public Company Company { get; private set; }

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

Возвращаясь к репозиторию, посмотрите на эту запись , чтобы получить хорошее объяснение IRepository поверх LinqToSql. В блоге Майка есть много других постов в репозиториях. Когда вы выберете ORM, я могу порекомендовать HHibernate вместо LinqToSql, последний теперь не существует, и у NHibernate есть отличное сообщество поддержки.

...