为什么要分开数据访问?
从这本书中,我认为模型驱动设计一章的前两页说明了为什么要从域模型的实现中抽象出技术实现细节的理由。
- 您想在域模型和代码之间保持紧密联系
- 分离技术问题有助于证明该模型对于实施是可行的
- 您希望无所不在的语言渗透到系统的设计中
这似乎都是为了避免使用一个单独的“分析模型”,而该模型与系统的实际实现脱离了。
从我对这本书的了解中可以看出,这种“分析模型”最终可以在不考虑软件实现的情况下进行设计。一旦开发人员尝试实现业务方面理解的模型,他们就会根据需要形成自己的抽象,从而造成沟通和理解上的障碍。
另一方面,开发人员在领域模型中引入太多技术问题也可能导致这种分歧。
因此,您可以考虑将关注点(如持久性)分离开来,可以帮助防止这些设计与分析模型之间的差异。如果有必要在模型中引入诸如持久性之类的东西,那么这是一个危险信号。也许该模型不适合实施。
报价:
“单一模型减少了出错的机会,因为设计现在是经过深思熟虑的模型的直接产物。设计甚至代码本身都具有模型的可通信性。”
我的解释方式是,如果最终有更多的代码行处理诸如数据库访问之类的事情,那么您将失去沟通能力。
如果访问数据库的需求是诸如检查唯一性之类的事情,请查看:
Udi Dahan:团队在应用DDD时犯的最大错误
http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-applying-ddd/
在“并非所有规则都平等”下
和
采用领域模型模式
http://msdn.microsoft.com/zh-CN/magazine/ee236415.aspx#id0400119
在“不使用域模型的方案”下,涉及同一主题。
如何区分数据访问
通过接口加载数据
“数据访问层”已经通过一个接口进行了抽象,您可以调用该接口来检索所需的数据:
var orderLines = OrderRepository.GetOrderLines(orderId);
foreach (var line in orderLines)
{
total += line.Price;
}
优点:该接口分离出“数据访问”管道代码,使您仍然可以编写测试。可以根据具体情况处理数据访问,从而比常规策略具有更好的性能。
缺点:调用代码必须假定已加载和未加载。
假设出于性能原因,GetOrderLines返回具有空ProductInfo属性的OrderLine对象。开发人员必须对接口背后的代码有深入的了解。
我已经在实际系统上尝试过这种方法。您最终一直在更改加载内容的范围,以尝试解决性能问题。您最终会在界面后面偷看,以查看数据访问代码以查看正在加载和正在加载的内容。
现在,关注点分离应该使开发人员可以尽可能一次地专注于代码的一个方面。接口技术删除了如何加载此数据,但未加载如何,何时加载以及何时加载。
结论:分离度很低!
延迟加载
数据按需加载。调用加载数据的操作隐藏在对象图本身中,在其中访问属性可以导致在返回结果之前执行sql查询。
foreach (var line in order.OrderLines)
{
total += line.Price;
}
优点:专注于域逻辑的开发人员对数据访问的“时间,地点和方式”是隐藏的。聚合中没有用于处理加载数据的代码。加载的数据量可以是代码所需的确切量。
缺点:当遇到性能问题时,如果拥有通用的“一刀切”解决方案,则很难修复。延迟加载可能会导致整体性能变差,并且实现延迟加载可能会比较棘手。
角色界面/渴望获取
通过聚合类实现的角色接口使每个用例都明确,从而允许按每个用例处理数据加载策略。
提取策略可能如下所示:
public class BillOrderFetchingStrategy : ILoadDataFor<IBillOrder, Order>
{
Order Load(string aggregateId)
{
var order = new Order();
order.Data = GetOrderLinesWithPrice(aggregateId);
return order;
}
}
然后,您的集合可能看起来像:
public class Order : IBillOrder
{
void BillOrder(BillOrderCommand command)
{
foreach (var line in this.Data.OrderLines)
{
total += line.Price;
}
etc...
}
}
BillOrderFetchingStrategy用于构建聚合,然后聚合执行其工作。
优点:允许每个用例使用自定义代码,以实现最佳性能。符合接口隔离原则。没有复杂的代码要求。聚合单元测试不必模仿加载策略。通用加载策略可用于大多数情况(例如“全部加载”策略),并在必要时实施特殊的加载策略。
缺点:更改域代码后,开发人员仍然必须调整/查看获取策略。
使用获取策略方法,您仍然可能会发现自己正在更改自定义获取代码以更改业务规则。这不是一个完美的关注点分离,但是最终将更易于维护,并且比第一种选择更好。提取策略确实封装了HOW,WHEN和WHERE数据的加载方式。它具有更好的关注点分离,而又不会失去灵活性,就像一种尺寸适合所有延迟加载方法一样。