Неправильно ли информировать доменные объекты об уровне доступа к данным?
В настоящее время я работаю над переписыванием приложения для использования Data Mappers, которые полностью абстрагируют базу данных от слоя Domain. Тем не менее, мне сейчас интересно, какой из них лучше подходит для обработки отношений между объектами домена:
- Вызовите необходимый метод find() из соответствующего преобразователя данных непосредственно в доменном объекте
- Запишите логику отношений в собственный преобразователь данных (что обычно делают примеры в PoEAA), а затем вызовите встроенную функцию отображения данных в объекте предметной области.
Либо мне кажется, что для сохранения мантры "Толстая модель, Skinny Controller" доменные объекты должны знать о преобразователях данных (независимо от того, являются ли они собственными или имеют доступ к другим преобразователям в системе)., Кроме того, кажется, что вариант 2 излишне усложняет уровень доступа к данным, поскольку он создает логику доступа к таблице для нескольких сопоставителей данных, а не ограничивает ее одним сопоставителем данных.
Итак, неправильно ли информировать объекты домена о соответствующих средствах отображения данных и вызывать функции средства отображения данных непосредственно из объектов домена?
Обновление: это единственные два решения, которые я могу представить для решения проблемы отношений между объектами домена. Любой пример, показывающий лучший метод, будет приветствоваться.
4 ответа
Боюсь, вы немного неправильно поняли намерение шаблона 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;
}
Другой путь - вместо вызова хранилища навыков. Пусть в этом примере хранилище сотрудников вызовет хранимую процедуру для загрузки набора результатов для навыков, а затем делегирует фабрику навыков, чтобы получить список навыков.
Разве я не могу позвонить в хранилище, и вызывает ли он вызов средства отображения данных или загружает объект в память, не так ли? - gabriel1836
Абсолютно верно. Таким образом я обычно макетирую весь уровень данных в своих модульных тестах.
Да. Спросите себя, почему объект домена знает о такой вещи? Даже не почему, а как? Вы собираетесь ввести свой 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);
}
}
Как видите, мои потребности в доступе к данным никак не влияют на дизайн моего домена.
Я не согласен, я думаю, что доменный объект может получить доступ к хранилищам через 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.
После некоторого дальнейшего чтения и поиска подходящего шаблона я наткнулся на шаблон репозитория.
Из того, что я вижу, это как раз и есть предполагаемое решение, которое позволяет доменным объектам, таким как Person, правильно делегировать запросы соответствующему преобразователю данных, оставляя объект домена и преобразователь данных полностью абстрагированными.