Design patterns 让域对象知道数据访问层是否不正确?
我目前正在重写一个应用程序,使用数据映射器从域层完全抽象数据库。但是,我现在想知道处理域对象之间关系的更好方法是什么:Design patterns 让域对象知道数据访问层是否不正确?,design-patterns,orm,domain-driven-design,poeaa,Design Patterns,Orm,Domain Driven Design,Poeaa,我目前正在重写一个应用程序,使用数据映射器从域层完全抽象数据库。但是,我现在想知道处理域对象之间关系的更好方法是什么: 直接从域对象内的相关数据映射器调用必要的find()方法 将关系逻辑写入本机数据映射器(PoEAA中的示例通常会这样做),然后在域对象内调用本机数据映射器函数 或者在我看来,为了保留“胖模型,瘦控制器”的咒语,域对象必须知道数据映射器(无论是它们自己的映射器还是它们可以访问系统中的其他映射器)。此外,选项2似乎不必要地使数据访问层复杂化,因为它跨多个数据映射器创建表访问逻辑,而
更新:这是我能想到的处理域对象之间关系问题的唯一两个解决方案。任何显示更好方法的示例都是受欢迎的。是。问问自己,为什么域对象会知道这样的事情?甚至不是为什么,而是如何?您要将DAL注入域对象吗 域应该遵循SRP,只需生活在其他方面。当您遍历您的域时,您应该不知道这些属性是通过延迟加载填充的,还是通过实例化生成的 我已经编写了一个域模型,其中包含DAL对象,维护起来真是一场噩梦。然后我了解了NHibernate和我的域由POCO和它们各自的业务逻辑组成,我想封装它们 [编辑] 这里有更多信息。如果我试图解释的话,我只会让自己难堪。我只能以用户的身份谈论实现。这里有一篇关于这个问题的好文章。您感兴趣的是拦截器和mixin的实现 使用这些工具,您可以编写employee类,如下所示:
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);
}
}
公共类员工
{
公职人员()
{
技能=新列表();
}
公共字符串名称{get;set;}
公共IList技能{get;private set;}
公共技能(技能)
{
//在此处执行特定于域的验证。。。
技能。添加(技能);
}
}
正如您所看到的,我的数据访问需求不会影响我的域设计。在进一步阅读并搜索合适的模式后,我偶然发现了
据我所见,这正是预想中的解决方案,它允许像Person这样的域对象将查询正确地委托给适当的数据映射器,同时使域对象和数据映射器完全抽象。恐怕您对存储库模式的意图有点误解 存储库的行为类似于特定域对象的内存集合,通常是聚合根:
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);
}
interface EmployeeRepository
{
列表检索者(条件someCriteria);
无效存储(员工或员工);
int countOf(标准someCriteria);
//便利方法
员工检索id(字符串id);
员工检索BYSN(字符串ssn);
}
这段代码的客户机不知道集合是否在内存中,就像单元测试一样,或者在某些情况下与ORM映射器交谈,或者在其他情况下调用存储过程,或者为某些域对象维护缓存
这仍然不能回答你的问题。事实上,域对象可能具有save()和load()方法,这些方法可以委托给正确的存储库。我认为这不是正确的方法,因为持久性几乎从来都不是业务领域的一部分,它给了你的领域对象不止一个改变的理由
看看有没有更多的闲话
针对对该答案的一些评论:
合理的批评。不过,我还是很担心 然后困惑于如何获得一个单一的 域对象或 在 现有域对象的上下文。 -加布里埃尔1836 假设一个员工有很多技能。我认为员工存储库这样调用技能存储库没有什么错:
// 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;
}
//假设EmployeeRepository通过存储过程与数据库对话
公共雇员检索id(字符串id)
{
ResultSet employeeResultSet=this.database.callSproc(“PROC_GetEmployeeById”,
新对象[]{id});
列出技能=
new SkillRepository().retrieveBy(新的EqualsCriteria(“EmployeeId”,id));
Employee=new EmployeeFactory().createNew()。
fromResultSet(employeeResultSet)。
技术(技能)。
build();
回归重建;
}
另一种方法不是调用技能库,而是让员工库调用存储过程(在本例中)来加载技能的结果集,然后委托给技能工厂以获取技能列表
我不能打电话到存储库吗 以及是否向 数据映射器或加载对象 它关注的是记忆,不是吗 加布里埃尔1836
完全正确。我通常用这种方式模拟单元测试中的整个数据层。我不同意,我认为域对象可以通过抽象工厂访问存储库
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;
}
}
公共类客户端
{
公共void ChangeZipCode(字符串zipCode)
{
//此方法根据工厂上下文访问内存或数据库
bool zipCodeExists=RepositoryFactory.Get().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>();
}