DDD-实体不能直接访问存储库的规则
||
在域驱动设计中,似乎有很多共识,即实体不应直接访问存储库。
这是来自Eric Evans域驱动设计书,还是来自其他地方?
背后的原因在哪里有很好的解释?
编辑:澄清:我不是在谈论将数据访问从业务逻辑分离到一个单独的层的经典OO做法-我是在谈论DDD中的具体安排,实体不应与完全是数据访问层(即,它们不应保存对存储库对象的引用)
更新:我给了BacceSR赏金,因为他的回答似乎最接近,但是我对此仍然很不了解。如果它是如此重要的原理,那么肯定在某处在线有关于它的好文章吗?
更新:2013年3月,对该问题的投票暗示对此有很多兴趣,尽管有很多答案,但我仍然认为,如果人们对此有想法,还有更多的余地。
没有找到相关结果
已邀请:
12 个回复
抬澈帅沮
瞥同忙接
他接着还提到了如何将域服务与双调度一起用于Aggregate命令方法的另一种解决方案。 (我不能推荐足够多的方法来阅读他的书。在您厌倦了无休止地在互联网上翻阅后,请付清当之无愧的钱来阅读这本书。) 然后,我与总是和gra可亲的Marco Pivetta @Ocramius进行了讨论,他向我展示了一些有关从域中提取规范并使用该规范的代码: 1)不建议这样做:
2)在域服务中,这很好:
乏摩纶誊伟
淑灯
优点:该接口分离出“数据访问”管道代码,使您仍然可以编写测试。数据访问可以根据情况进行处理,从而比常规策略具有更好的性能。 缺点:调用代码必须假定已加载和未加载。 假设出于性能原因,GetOrderLines返回具有空ProductInfo属性的OrderLine对象。开发人员必须对接口背后的代码有深入的了解。 我已经在实际系统上尝试过这种方法。您最终一直在更改加载内容的范围,以尝试解决性能问题。您最终会在界面后面偷看,以查看数据访问代码以查看正在加载和正在加载的内容。 现在,关注点分离应该使开发人员可以尽可能一次地专注于代码的一个方面。接口技术删除了如何加载此数据,但未加载如何,何时加载数据以及在何处加载。 结论:分离度很低! 延迟加载 数据按需加载。调用加载数据的操作隐藏在对象图本身中,在其中访问属性可以导致在返回结果之前执行sql查询。
优点:专注于域逻辑的开发人员无法隐藏数据的“何时,何地和如何”。聚合中没有用于处理加载数据的代码。加载的数据量可以是代码所需的确切量。 缺点:当遇到性能问题时,如果拥有通用的“一刀切”解决方案,则很难修复。延迟加载可能会导致整体性能变差,并且实现延迟加载可能会比较棘手。 角色界面/渴望获取 通过聚合类实现的角色接口使每个用例都明确,从而允许按每个用例处理数据加载策略。 提取策略可能如下所示:
然后,您的聚合看起来像:
BillOrderFetchingStrategy用于构建聚合,然后聚合执行其工作。 优点:允许每个用例使用自定义代码,以实现最佳性能。符合接口隔离原则。没有复杂的代码要求。聚合单元测试不必模仿加载策略。通用加载策略可用于大多数情况(例如“全部加载”策略),并在必要时实施特殊的加载策略。 缺点:更改域代码后,开发人员仍然必须调整/查看获取策略。 使用获取策略方法,您仍然可能会发现自己正在更改自定义获取代码以更改业务规则。这不是关注点的完美分离,但是最终将更易于维护,并且比第一种方法更好。提取策略确实封装了HOW,WHEN和WHERE数据的加载方式。它具有更好的关注点分离,而不会失去灵活性,就像一种尺寸适合所有延迟加载方法一样。
抽法
皇小福另届
我们希望在不将任何服务注入实体的构造函数中的情况下实现此目标,因为: 引入新行为(使用新服务)可能会导致构造函数更改,这意味着更改会影响实例化实体的每一行! 这些服务不是模型的一部分,但是构造函数注入将表明它们是模型的一部分。 服务(甚至是其接口)通常是实现细节,而不是域的一部分。域模型将具有面向外部的依赖关系。 令人困惑的是,如果没有这些依赖项,实体就无法存在。 (您说信用证服务吗?我什至不会对信用证做任何事情...) 这将使其难以实例化,因此难以测试。 这个问题很容易传播,因为其他包含此实体的实体将获得相同的依赖关系-在它们看来可能是非常不自然的依赖关系。 那么,我们该怎么做呢?到目前为止,我的结论是方法依赖和双重调度提供了不错的解决方案。
现在需要负责创建贷方通知单的服务。它使用双重调度,将工作完全移交给负责的服务,同时保持了来自“ 10”实体的可发现性。 ѭ11现在对记录器有一个简单的依赖关系,显然它将完成部分工作。 对于后者,为了使客户端代码更容易处理,我们可以改用might12ѭ登录。毕竟,发票记录似乎是发票所固有的。这样的单个“ 12”有助于避免为各种操作而需要各种微型服务。不利的一面是,该服务的确切功能变得晦涩难懂。它甚至可能看起来像是双重调度,而大部分工作实际上仍在ѭ11本身完成。 我们仍然可以将参数命名为“ logger”,以期揭示我们的意图。不过似乎有些虚弱。 取而代之的是,我选择要求
(就像我们在代码示例中所做的那样),并让
实现该接口。客户代码可以简单地将单个
用于要求任何这种非常特殊的,发票固有的“迷你服务”的
方法,而方法签名仍然清楚地表明了他们的要求。 我注意到我没有明确地访问存储库。记录器是或使用存储库,但让我也提供一个更明确的示例。如果只需一两个方法就需要存储库,我们可以使用相同的方法。
实际上,这为麻烦的惰性负载提供了替代方案。 更新:我出于历史目的保留了以下文字,但我建议避免100%的延迟加载。 对于真正的,基于属性的延迟加载,我目前确实使用构造函数注入,但是以一种对持久性无知的方式。
一方面,从数据库中加载“ 10”的存储库可以自由访问将加载相应贷方通知单的功能,并将该功能注入“ 10”中。 另一方面,创建实际的新ѭ10的代码将仅传递返回空列表的函数:
(自定义“ 25”可以使我们摆脱“ 26”的丑陋表象,但这会使讨论变得复杂。)
我很高兴听到您的意见,偏好和改进!
捻盒愧杯
先对冈蒲
琳娘
舶啥戚
屡倒雷图
瞧叮