在这个问题中有人回答 "你永远不会让域对象实现自己调用服务!".这个陈述是DDD的一个严格的快速规则,还是取决于您自己的应用程序和架构?
举例:
举个例子,我们假设我们UserImage
的模型中有一个对象,它由用户从上传的图像中填充.然后我们假设我们可以将此图像提交给可识别拇指打印的第三方服务,Guid
如果找到匹配则返回.
public IThumbPrintService { Guid FindMatch(Bitmap image); } public class UserImage { public Bitmap Image {get; set;} public Guid ThumbPrintId {get; set;} public bool FindThumbPrintMatch() { // Would you call the service from here? ThumbPrintId = _thumbPrintService.FindMatch(this.Image); return ! ThumbPrintId.CompareTo(Guid.Empty); } } public class RoboCopUserImageService : IUserImageService { // Or move the call to a service method // since it depends on calling a separate service interface public bool FindThumbPrintMatch(UserImage userImage) { userImage.ThumbPrintId = _thumbPrintService.FindMatch(userImage.Image); return !userImage.ThumbPrintId.CompareTo(Guid.Empty); } }
不让域对象自己调用服务可以避免或获得什么?
编辑:有没有讨论这个特定主题的好的在线文章?
这是电子表格难题:电话是否拨打电话号码,或电话号码是否在电话上自动拨号?
你可能会发现Double Dispatch是有趣的阅读,虽然你的情况有点过分,我估计.
该单一职责原则是在与OO宗旨赔率通常的告诉,不要问.我对这个问题的看法已经发生了变化,当逻辑应该进入域对象时,我已经确定了以下条件:
逻辑应该是域逻辑,即没有像image.load()或image.save()
逻辑不应该给对象提供更多的"一个理由"来改变域.这是SRP的重新声明.
逻辑不应强制进入对象.换句话说,如果您最终尝试在不使用getter的情况下尝试计算域对象列表中某些属性的总和,请三思而后行.
在您的情况下,我选择不对实体对象内的服务进行调用,主要是因为该服务似乎与您的域无关,而是与持久性更相关.域对象应该与域概念耦合,我认为您提供的服务不符合要求.
如果您的应用程序使用第三方工作流服务器来管理其部分状态,我认为在实体中调用服务可能是可接受的示例.本质上,这是状态模式,其中状态在运行时定义.
我认为使用domainObject.moveToNextState()(假设此代码在您无处不在的语言中"有意义")调用与您的服务器通信的服务是可以接受的,因为工作流服务器管理域模型的一部分.
我将补充说,DDD对遵循域的语言非常感兴趣.您是否听过域专家说"如果用户图像的拇指打印与XYZ供应商服务中的匹配相匹配"?或者他们说"XYZ供应商服务,给定拇指印,表明是否存在拇指印"?选择在您的域中最有意义的那个.
更多的想法(我已经考虑过这个问题,因为它是设计的核心):
在Evans DDD一书中,账户实体具有信用(金额),借记(金额),转账(账户,金额)和累计()等方法,但FundsTransferService具有转账(账户,账户,金额)方法.transferTo方法不会调用任何服务,而只是处理涉及帐户的逻辑,例如贷记和记入正确的金额.
除了协调之外,FundsTransferService还有自己的规则来检查,这些规则不适合账户.信用卡或借记卡的确切金额可能涉及外部各方.这使transferTo调用服务变得尴尬.
对于简单的对象,比如UserImage,可以适应对象本身的重要域逻辑可能很少,因为据我所知,它不是一个聚合.我认为,聚合体提供了更多容纳域逻辑的机会.Account示例可能是Aggregate.