为什么要分离数据访问?
从书中,我认为模型驱动设计一章的前两页给出了一些理由,说明为什么要从领域模型的实现中抽象出技术实现细节。
您希望在领域模型和代码之间保持紧密的连接
分离技术关注点有助于证明模型对于实现是可行的
你希望无处不在的语言渗透到系统的设计中
这似乎都是为了避免独立的“分析模型”与系统的实际实现分离。
根据我对这本书的理解,它说这个“分析模型”可以在不考虑软件实现的情况下被设计出来。一旦开发人员试图实现业务方面理解的模型,他们就会由于需要而形成自己的抽象,从而在交流和理解中造成障碍。
在另一个方向上,开发人员在领域模型中引入太多的技术关注也会导致这种分歧。
因此,您可以考虑实践关注点分离,例如持久性,可以帮助防止这些设计和分析模型出现分歧。如果觉得有必要在模型中引入持久性之类的东西,那么这就是一个危险信号。也许这个模型对于实现来说并不实用。
引用:
“单一模型减少了出错的可能性,因为现在的设计是经过仔细考虑的模型的直接产物。设计,甚至代码本身,都具有模型的交流性。”
我对此的解释是,如果你最终要用更多行代码来处理数据库访问之类的事情,你就失去了交流能力。
如果需要访问数据库是为了检查唯一性之类的事情,请查看:
Udi Dahan:团队在应用DDD时所犯的最大错误
http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-applying-ddd/
"所有规则都是不平等的"
and
使用领域模型模式
http://msdn.microsoft.com/en-us/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用于构建聚合,然后聚合完成它的工作。
优点:允许为每个用例定制代码,从而实现最佳性能。符合界面隔离原则。没有复杂的代码需求。聚合单元测试不必模拟加载策略。一般的加载策略可以用于大多数情况下(例如“加载全部”策略),特殊的加载策略可以在必要时实现。
缺点:开发者在修改域代码后仍然需要调整/审查抓取策略。
使用获取策略方法,您可能仍然会发现自己在更改业务规则时更改自定义获取代码。这不是一个完美的关注点分离,但最终会更易于维护,并且比第一种选择更好。抓取策略确实封装了如何、何时和何处加载数据。它具有更好的关注点分离,而不会像“一刀切”的惰性加载方法那样失去灵活性。