【问题标题】:DDD with EF Code First - how to put them together?DDD 与 EF Code First - 如何将它们组合在一起?
【发布时间】:2012-10-24 11:52:03
【问题描述】:

我在学习 DDD 开发这几天,我开始喜欢它了。

我(我想我)了解 DDD 的原理,您的主要关注点是业务对象,您有聚合、聚合根、仅用于聚合根的存储库等等。

我正在尝试创建一个简单的项目,将 DDD 开发与 Code First 方法相结合。

我的问题是:(我正在使用 asp.net MVC)

  1. DDD 业务对象将不同于 Code First 对象? 即使它们可能相同,例如我可以有一个 Product 业务对象,它包含所有规则和方法,我可以有一个 Product 代码优先 (POCO) 对象,它只包含我需要的属性保存在数据库中。

  2. 1234563我正在使用“AutoMapper”或类似的东西? 如果答案是“不”,我完全迷失了。

你能告诉我如何将这两者放在一起的最简单 (CRUD) 示例吗?

谢谢

【问题讨论】:

标签: c# asp.net-mvc ef-code-first domain-driven-design code-first


【解决方案1】:

更新我不再提倡使用“域对象”,而是提倡使用基于消息传递的域模型。示例见here

#1 的答案是视情况而定。在任何企业应用程序中,您都会在域中找到 2 个主要类别的东西:

直接 CRUD

这里不需要域对象,因为对象的下一个状态不依赖于对象的先前状态。都是数据,没有行为。在这种情况下,可以在任何地方使用相同的类(即 EF POCO):编辑、持久化、显示。

这方面的一个例子是在订单上保存帐单地址:

public class BillingAddress {
  public Guid OrderId;
  public string StreetLine1;
  // etc.
}

另一方面,我们有...

状态机

您需要有用于域行为和状态持久性的单独对象(以及用于完成工作的存储库)。域对象上的公共接口应该几乎总是所有 void 方法并且没有公共 getter。订单状态就是一个例子:

public class Order { // this is the domain object  
  private Guid _id;
  private Status _status;

  // note the behavior here - we throw an exception if it's not a valid state transition
  public void Cancel() {  
    if (_status == Status.Shipped)
      throw new InvalidOperationException("Can't cancel order after shipping.")
    _status = Status.Cancelled;
  }

  // etc...
}

public class Data.Order { // this is the persistence (EF) class
  public Guid Id;
  public Status Status;
}

public interface IOrderRepository {
  // The implementation of this will:
  // 1. Load the EF class if it exists or new it up with the ID if it doesn't
  // 2. Map the domain class to the EF class
  // 3. Save the EF class to the DbContext.
  void Save(Order order); 
}

#2 的答案是 DbContext 将自动跟踪对 EF 类的更改。

【讨论】:

    【解决方案2】:

    答案是否定的。EF 代码优先的优点之一是它非常适合 DDD,因为您必须手动创建业务对象,因此请务必使用您的 EF 模型来等效于 DDD 实体和值对象.无需增加额外的复杂性,我认为 DDD 不建议在任何地方这样做。

    您甚至可以让您的实体实现 IEntity,并为对象赋值以实现 IValue,此外还可以遵循其他 DDD 模式(即存储库)来与数据库进行实际通信。更多这些想法你可以在 .NET 中找到这个非常好的示例应用程序,即使它不首先使用 EF 代码,它仍然非常有价值:http://code.google.com/p/ndddsample/

    【讨论】:

    • Code First 确实允许您创建类而不用担心 DB,但我不认为它们可以替换为域对象,它有很多私有属性、方法、只读字段,所有这些字段都不能被 EF 正确映射。这就是为什么我认为你应该有两个对象
    • 你可以拥有你提到的所有东西,你可以在映射中忽略它们或将它们映射到不同的列名等......映射是 IRepository 实现的一部分,并成为实现细节不会污染您的模型。在所有情况下,好的设计都是关于管理复杂性的,因此即使您没有严格遵守 DDD,您最好还是管理一个业务对象层。如果您有一个要连接的旧数据库并希望拥有一个干净的模型然后将其映射到与您的数据库匹配的另一个模型,那么您可能需要这种映射的唯一时间
    • +1 同意第一段,但在 DDD 的上下文中,我也同意 OP,即 EF 实体不能被视为域模型,因为我刚刚意识到 EF 不支持映射一个数据库表到多个实体。域模型需要手动创建/映射(使用 AutoMapper/ValueInjecter)。这必须在业务对象中完成。它增加了框架的复杂性,但我认为会减少业务开发。
    • 以上关于值对象的信息不正确。根据定义,值对象没有 ID,实体框架(以及一般的关系数据库)不支持这一点。
    • EF6 确实支持值对象/复杂类型
    【解决方案3】:

    最近我做了类似的项目。我正在关注本教程:link 我是这样做的:我创建了空白解决方案,添加了项目:域、服务和 WebUI。

    简单地说,我在域中放置了模型(例如 EF 代码的类,方法等) 服务用于域使用 asp.net webapi 与世界(WebUI、MobileUI、其他站点等)进行通信 WebUi 实际上是 MVC 应用程序(但模型在域中,所以它主要是 VC)

    希望我有所帮助

    【讨论】:

      【解决方案4】:

      Pluralsight 课程:Entity Framework in the Enterprise 进入了与 EF Code First 相结合的领域驱动设计的确切场景。

      对于 1 号,我相信你可以做到这一点。这只是风格问题。 对于第 2 点,视频中的讲师通过几种方式来解释这一点。一种方法是在修改值时在客户端设置的每个类上都有一个“状态”属性。然后 DbContext 知道哪些更改要持久化。

      【讨论】:

      • 谢谢,但很遗憾我无法从那里下载教程。 (我没有信用卡可以注册)
      • 这个 Pluralsight 课程有点乱,Julie 会教你如何使用通用存储库反模式以及如何构建大量无用的抽象来让你的代码更复杂
      • 嘿,在过去的 2 年里我学到了很多东西,除了最简单的 CRUD 场景之外,我不再喜欢通用 repos!我将使用自构建上一个版本以来一直在学习的 DDD 内容更新该 EF Enterprise 课程。课程。 Plus EF 现在支持更顺畅地模拟更少的抽象。希望你会更喜欢 udpate,ILCH。 :)
      【解决方案5】:

      关于这个主题的最新问题。 阅读 Josh Kodroff 的回答证实了我对将存储库实施到例如实体框架 DAL 的想法。

      您将域对象映射到 EF 持久性对象,并在保存时让 EF 处理它。 检索时,您让 EF 从数据库中获取并将其映射到您的域对象(聚合根)并将其添加到您的集合中。

      这是存储库实施的正确策略吗?

      【讨论】:

      • 如果您不介意在域模型上做出一些妥协。持久性基础设施通常会对域模型产生一些副作用,使其适合持久性(但有时会冗余域或完全改变您的模型)。
      • 这个答案更适合作为评论或单独的问题。
      • 是的。抱歉,但我刚刚注册并没有 50 个代表点允许我评论 Josh 的回答。感谢您的 cmets,感谢 Hippoom,我的想法完全正确。
      猜你喜欢
      • 2014-09-28
      • 2011-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多