【问题标题】:Send domain entity as paremeter or send entity id as parameter in application services在应用服务中发送域实体作为参数或发送实体 id 作为参数
【发布时间】:2011-07-15 10:06:05
【问题描述】:

在使用域驱动设计时,您的服务方法接收实体作为参数还是接收实体的 id 作为参数更好,以便您可以使用存储库检索方法内的实体?

例如:

public void Apply(Job job, User user)

public void Apply(int jobId, int userId)

【问题讨论】:

  • 申请job的是user,对吧? user.Apply(job) 怎么样。我知道,应用程序服务呢?我与 Java 人群进行了讨论,他们在这方面给了我很大的打击。 DDD 并不是要忘记 OOP。是的,user 申请job
  • @ryudice 肯定是可以测试的。
  • @MikeEast 虽然您的 OO 建议是很好的建议,但问题有点抽象——特别是关于应该如何形成参数。同意了吗?
  • @ryudice 问题的措辞是否更清楚:“如何将域实体封装为方法参数?id 整数或实体实例?”

标签: c# architecture domain-driven-design


【解决方案1】:

我使用 ViewModel 向/从应用程序服务传递“平面实体”。我还使用文档消息模式与应用程序服务进行通信。在应用程序服务中,为业务实体使用扩展方法,如下所示:

public BusinessEntityViewModel ConvertToViewModel(this BusinessEntity businessEntity)
{
      BusinessEntityViewModel bevm = new BusinessEntityViewModel{ Id = businessEntity.Id, ...}
      return bevm;
}

【讨论】:

    【解决方案2】:

    DDD 是关于将客户的词汇映射到您的设计。您的客户(希望)谈论的是申请工作的用户,而不是某个整数 ID 链接到另一个整数 ID。尽量贴近真实世界,除非它成为负担。

    通过传入整个实体,您可以立即从实体的行为中受益,而无需先构造它。

    所以坚持使用实体,除非您遇到经常只有一个 ID 可以使用的情况。这通常发生在您处理外部系统(例如网络服务)时。在这种情况下,您可以创建一个接受 ID 的方法重载。此重载应验证 ID、构造实体并调用接受实体的重载。

    public void Apply(int jobId, int userId)
    {
        // Validate the IDs.
    
        // Construct the entities.
        var job = ...;
        var user = ...;
    
        // Call the 'proper' overload.
        Apply(job, user);
    }
    
    public void Apply(Job job, User user)
    {
        // Actual business logic belongs here.
    }
    

    再次重申:尽量避免此类重载,除非您正在处理只返回 ID 的外部系统。

    从编码的角度来看,实体也比整数好。整数可以是任何整数值,因此您必须在使用它的任何地方验证它。您的工厂负责构建有效实体,并且您的(域)逻辑应使您的实体处于有效状态。所以使用实体是完全安全的,不需要太多验证。

    【讨论】:

      【解决方案3】:

      这取决于您的应用程序服务所在的位置:

      如果您的服务在 同一个 AppDomain 内执行(即您从同一个可执行文件中调用它),那么传递对象是理想的。 Service 可以轻松访问对象图中的其他对象。

      如果您的服务在不同的 AppDomain 中执行(例如,在 WebService 或其他远程位置之后),您应该使用 ID。然后,服务会在使用它之前从持久性存储中加载适当的对象。

      如果您尝试通过网络发送对象,您可能会遇到problems described here

      希望对您有所帮助。

      【讨论】:

        【解决方案4】:

        在我的理解中,区别仅在于 Domain 对 User 和 Job 做了什么来进行“应用”操作。如果 ID 够用,那么只留下 ID 就可以了。

        【讨论】:

          【解决方案5】:

          如果您发送对象,您将在服务和实体之间创建依赖关系。使用服务的优点之一是充当外观以减少类之间的依赖图并降低设计的复杂性。最好在服务契约中使用原始类型、结构、枚举,但是在处理大量方法参数时,您可以将它们封装在一个对象中,但在这种情况下,它将是一个 DTO,而不是一个实体。

          【讨论】:

          • 如果他传入一个接口怎么办?说:Apply(IJob, IUser) ?
          • @Adrian K 与我在回答中解释的优势相同 - 它们是类型,因此它比原语更好。使用合同而不是混凝土的决定将取决于您的设计/架构。这与如何将模型对象封装为参数的问题并不真正相关。
          • @Adrian 接口不应该定义结构而是定义行为。
          • @cottsak:我在这里写我的 cmets,因为它是唯一允许的地方:).. 根据定义,实体必须有一个 ID。没有 ID 的对象是不可变的值类型对象,例如 Money、Address、...。服务出现在需要构建屏障以隐藏事物的模型中,并且旨在供外部人员使用以获取有关内部内容的信息。如果该服务用于业务逻辑或与其他系统交互,则没有区别。这只是规模或粒度的问题。
          • @cottask 如果外部实体引用了服务背后的实体,您将在任何地方获得相互依赖关系。如果您要更改一个类,您将必须审查、修改、编译和测试所有内容。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-03
          相关资源
          最近更新 更多