Domain driven design 域对象和服务

Domain driven design 域对象和服务,domain-driven-design,Domain Driven Design,有人质疑“你永远不会让域对象实现自己调用服务!”。这是DDD的硬性规定,还是取决于您自己的应用程序和体系结构 人为的例子: 例如,假设我们的模型中有一个UserImage对象,该对象由用户从上传的图像中填充。然后假设我们可以将此图像提交给第三方服务,该服务可以识别指纹,并在找到匹配项时返回Guid public IThumbPrintService { Guid FindMatch(Bitmap image); } public class UserImage { public

有人质疑“你永远不会让域对象实现自己调用服务!”。这是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);            
     }
}
不让域对象自己调用服务可以避免或获得什么


编辑:有什么好的在线文章讨论这个特定主题吗?

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

如果允许实体对象调用服务,则它将执行数据对象和服务对象两个角色。一般来说,每个对象不仅在实现上,而且在使用上都应该有责任

在您的例子中,低级用户图像似乎既是图像又是指纹识别器。

这是:

不过,你可能会发现这本书很有趣

问题是。我对这个主题的感觉发生了波动,当逻辑应该进入领域对象时,我已经确定了以下条件:

  • 逻辑应该是域逻辑,即不像image.load()或image.save()那样
  • 逻辑不应该给对象多个在域中更改的“理由”。这是SRP的重新声明
  • 逻辑不应该被强制到对象中。换言之,如果你最终做了什么,三思而后行
在您的情况下,我会选择不将对服务的调用放在实体对象中,主要是因为服务似乎与您的域无关,而与持久性更相关。域对象应该与域概念相耦合,我认为您提供的服务不合格

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

我认为可以让domainObject.moveToNextState()调用与服务器对话的服务(假设此代码在您的通用语言中“有意义”),因为工作流服务器管理域模型的一部分

我要补充的是,DDD对遵循该领域的语言非常感兴趣。您是否听到域专家说“用户图像会发现其指纹是否与XYZ供应商服务中的指纹匹配”?或者他们会说“XYZ供应商服务,给定一个指纹,指示该指纹是否存在”?选择在你的领域最有意义的

还有一些想法(我对这个问题考虑了很多,因为它是设计的核心):

  • 在Evans DDD的书中,一个帐户 实体有如下方法 贷方(金额)、借方(金额)、转账(账户、金额)和 Account(),但FundsTransferService具有转账(帐户、帐户、金额)方法。transferTo方法不调用任何服务,只处理涉及帐户的逻辑,如记入正确金额的贷方和借方

    除了协调外,FundsTransferService还有自己的规则需要检查,这些规则不适合账户。贷记或借记的确切金额可能涉及外部各方。这使得transferTo很难呼叫该服务

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

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

该示例是当场编写的。真正的问题是你的域对象是否应该调用服务,而电子表格的难题几乎一针见血。这要看情况了!谢谢你澄清了你的答案。如果您的实体对象具有Account.Debit(amount)等方法,需要使用服务来完成操作(例如:更新存储库、调用验证服务或发送通知),该怎么办?它将通过让AccountService接受简单的Account对象作为Debit方法的参数来处理此问题。谢谢,电子表格的难题让这个问题变得更加清晰。我也喜欢你建议将问题口头告知领域专家,以确定哪种方法更有意义。哇,回答很好,非常清楚,非常详细。我想给你多投一票!谢谢你的表扬。这个答案的内容实际上是综合了我未确诊的强迫症的影响。DDD和OO提出的问题总是让我着迷。