域对象和服务

时间:2009-01-12 21:23:42

标签: domain-driven-design

this问一个人replies“你永远不会让域对象实现自己调用服务!”。这个陈述是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);            
     }
}

不让域对象自己调用服务可以避免或获得什么?

编辑:是否有讨论此特定主题的优秀在线文章?

4 个答案:

答案 0 :(得分:45)

这是Spreadsheet Conundrumdoes the phone dial the phone number, or does the phone number dial itself on the phone

你可能会发现Double Dispatch是有趣的阅读,overkill in your situation, I reckon

Single Responsibility Principleoften at odds with the OO tenetTell, Don't Ask。我对这个问题的看法已经发生了变化,当逻辑进入域对象时,我已经确定了以下条件:

在您的情况下,我选择不对实体对象内的服务进行调用,主要是因为该服务似乎与您的域无关,而是与持久性更相关。域对象应该与域概念相结合,我认为您提供的服务不符合要求。

如果您的应用程序使用第三方工作流服务器来管理其部分状态,我认为在实体中调用服务可能是可接受的示例。从本质上讲,这是State Pattern,其中状态在运行时定义。

我认为使用domainObject.moveToNextState()(假设此代码在您无处不在的语言中“有意义”)调用与您的服务器通信的服务是可以接受的,因为工作流服务器管理域模型的一部分。

我将补充说DDD对遵循域的语言非常感兴趣。您是否听过域专家说“如果用户图像的拇指打印与XYZ供应商服务中的匹配相匹配”?或者他们说“XYZ供应商服务,给定拇指印,表明是否存在拇指印”?选择在您的域中最有意义的那个。

更多的想法(我已经考虑过这个问题,因为它是设计的核心):

  • 在埃文斯DDD书中,一个帐户 实体有类似的方法 信用(金额),借方(金额),转账(账户,金额)和 accrue(),但是FundsTransferService有一个转移(账户,账户,金额)方法。 transferTo方法不会调用任何服务,而只是处理涉及帐户的逻辑,例如贷记和记入正确的金额。

    除了协调之外,FundsTransferService还有自己的规则来检查,这些规则不适合账户。信用卡或借记卡的确切金额可能涉及外部各方。这使transferTo调用服务变得尴尬。

  • 对于像UserImage这样的简单对象,可能适合对象本身的重要域逻辑可能很少,因为据我所知,它不是一个聚合。我认为,聚合体提供了更多容纳域逻辑的机会。 Account示例可能是Aggregate。

答案 1 :(得分:2)

我看到的一个缺点是,允许域对象调用服务可能会使序列化变得更加困难,或者在另一方的某个人调用其服务方法时序列化后至少会导致一些问题。

答案 2 :(得分:0)

如果允许实体对象调用服务,则它将执行两个角色Data Object和Service Object。通常,每个对象都应该承担责任,不仅在实施中,而且在使用中。

在您的情况下,低级UserImage似乎既是Image又是ThumbPrint识别器。

答案 3 :(得分:0)

我认为最好不要从实体或值对象调用存储库或服务,但有时它是必要的,例如,如果实体必须返回应该从数据库加载的其他实体但是它无法使用对象图导航到它。然后依赖倒置原则会有所帮助,这意味着实体和值对象依赖于服务和存储库的接口,而不依赖于实现。