Неправильно ли информировать доменные объекты об уровне доступа к данным? - PullRequest
8 голосов
/ 22 января 2009

В настоящее время я работаю над переписыванием приложения для использования Data Mappers, которые полностью абстрагируют базу данных от слоя Domain. Однако теперь мне интересно, какой подход лучше подходит для обработки отношений между объектами домена:

  1. Вызвать необходимый метод find () из соответствующего преобразователя данных непосредственно в доменном объекте
  2. Запишите логику отношений в собственный преобразователь данных (как это обычно делают примеры в PoEAA), а затем вызовите встроенную функцию отображения данных в объекте домена.

Либо мне кажется, что для сохранения мантры «Толстая модель, Skinny Controller» доменные объекты должны знать о преобразователях данных (будь то их собственные или они имеют доступ к другим преобразователям система). Кроме того, кажется, что вариант 2 излишне усложняет уровень доступа к данным, поскольку он создает логику доступа к таблице для нескольких сопоставителей данных, а не ограничивает ее одним сопоставителем данных.

Итак, неправильно ли информировать доменные объекты о связанных средствах отображения данных и вызывать функции отображения данных непосредственно из объектов домена?

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

Ответы [ 4 ]

7 голосов
/ 23 января 2009

Боюсь, вы немного неправильно поняли цель шаблона Repository.

Репозиторий должен вести себя как набор в памяти определенного объекта домена, обычно совокупного корня:

interface EmployeeRepository
{
    List<Employee> retrieveBy(Criteria someCriteria);
    void store(Employee someEmployee);
    int countOf(Criteria someCriteria);
    // convenience methods
    Employee retrieveById(String id);
    Employee retrieveBySSN(String ssn);
}

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

Это все еще не отвечает на ваш вопрос. Фактически, у вас могут быть доменные объекты с методами save () и load (), которые делегируют в правильный репозиторий. Я не думаю, что это правильный путь, потому что постоянство почти никогда не является частью бизнес-сферы, и это дает вашему объекту домена более чем одну причину для изменения.

Проверьте этот связанный вопрос , чтобы узнать больше.

В ответ на некоторые комментарии к этому ответу:

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

Допустим, у Сотрудника много навыков. Я не вижу ничего плохого в том, что репозиторий сотрудников вызывает репозиторий навыков, как это:

// assume EmployeeRepository talks to a DB via sprocs
public Employee retrieveById(String id)
{
    ResultSet employeeResultSet = this.database.callSproc("PROC_GetEmployeeById", 
        new Object[] { id });

    List<Skill> skills = 
        new SkillRepository().retrieveBy(new EqualsCriteria("EmployeeId", id));

    Employee reconstructed = new EmployeeFactory().createNew().
                                  fromResultSet(employeeResultSet).
                                  withSkills(skills).
                                  build();

    return reconstructed;    
}

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

Не могу я позвонить в репозиторий и выдает ли он вызов Data Mapper или загружает объект в памяти это его забота, не так ли? - gabriel1836

Совершенно верно. Я обычно таким образом копирую весь уровень данных в своих модульных тестах.

7 голосов
/ 22 января 2009

Да. Спросите себя, почему объект домена знает о такой вещи? Даже не почему, а как? Вы собираетесь внедрить свой DAL в объект Domain?

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

Я написал модель предметной области, в которой были объекты DAL, и это было ужасно. Затем я изучил NHibernate, и мой домен состоит из POCO и соответствующей бизнес-логики, которую я хочу инкапсулировать.

[редактировать]

Вот больше информации. Я бы только смутился, если бы попытался это объяснить. Я могу говорить только о реализации как пользователь. Вот отличная статья Управление моделью домена . Что вас интересует, так это реализация перехватчиков и миксинов.

С помощью этих инструментов вы можете написать класс сотрудника следующим образом:

public class Employee
{
    public Employee()
    {
        Skills = new List<Skill>();
    }

    public string Name { get; set; }
    public IList<Skill> Skills { get; private set; }

    public void AddSkill(Skill skill)
    {
        // Perform Domain specific validation here...

        Skills.Add(skill);
    }
}

Как видите, мои потребности в доступе к данным никак не влияют на дизайн моего домена.

0 голосов
/ 06 мая 2010

Я не согласен, я думаю, что доменный объект может получить доступ к хранилищам через Abstract Factory.

public class Client
{
  public void ChangeZipCode(string zipCode)
  {
    // This method access memory or database depending on the factory context
    bool zipCodeExists = RepositoryFactory.Get<IZipCode>().Exists(zipCode);
    this.zipCode = zipCode;
  }
}

Используя этот шаблон, нет необходимости внедрять интерфейсы репозитория по всему вашему коду. но только фабрика хранилищ.

public abstract class RepositoryFactory
{
  // Class operations
  private static _globalInstance;
  public static CreateGlobalInstance(RepositoryFactory factory)
  {
    _glocalInstance = factory;
  }
  public static I Get<I>()
  {
    return _globalInstance.Get<I>();
  }
  /////////////////////

  ///// this operation should be inherited by:
  ///// * NHibernateRepositoryFactory //
  ///// * Linq2SqlRepositoryFactory ////
  ///// * NUnitRepositoryFactory ///////      
  ///// it depends in your context ////////////////////////
  public abstract I GetRepository<I>();
}

Я делал это годами без проблем в моих юнит-тестах.

Поэтому внедрение зависимостей требуется только в этом классе RepositoryFactory.

0 голосов
/ 22 января 2009

Пройдя дальнейшее чтение и поиск подходящего шаблона, я наткнулся на шаблон репозитория .

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

...