关于将实体映射到域对象的建议

时间:2013-11-15 21:37:11

标签: c# entity-framework domain-driven-design

我目前正在开展一个项目,我们开始使用DDD方法构建应用程序。我们现在首先考虑使用Entity Framework 6代码来帮助我们处理数据持久性。我的问题是如何最好地处理我们的域对象和EF实体之间的数据映射?

4 个答案:

答案 0 :(得分:48)

为了保持你的应用程序和你自己长期保持理智,永远不要开始你的DDD应用程序与持久性相关的问题(什么数据库,什么orm等)和总是(是的,总是)触摸数据库作为开发的最后阶段

为您的域建模,事实上除持久性之外的任何其他模型。使用Repository模式使应用程序与Persistence分离。根据应用程序的需要定义repo接口,而不是绑定到db访问方法(这就是为什么你以后实现持久性的原因所以你不会想要将你的应用程序与持久性细节结合起来。)

为repo接口编写内存实现,这通常意味着对列表或字典的简单包装,因此编写速度非常快,更重要的是更改微不足道。使用它们来实际测试和开发应用程序。

在接口稳定且应用程序正常工作之后,就可以编写持久性实现,您可以在其中使用任何您想要的内容。在你的情况下,EF就是映射。

现在,这是非常主观的,没有正确或错误的方式,这就是你喜欢做事的方式。

就个人而言,我习惯使用纪念品,所以我从域对象中获取纪念品,然后手动将其映射到(微)ORM实体。我手动操作的原因是因为我的纪念品包含值对象。如果我要使用AutoMapper,我需要对它进行混淆,实质上我会编写更多代码而不是手动编写

更新(2015)

这些天我只是Json这个对象,要么使用特定的读取模型,要么将其直接存储在包含序列化对象的Data列的读取模型中。我仅在特定情况下使用Mementos。 < /更新>

根据您的域对象的外观以及EF实体的外观,您可以在大多数映射中使用automapper。但是,您将更难以测试您的存储库。

取决于你如何这样做,找到适合自己风格的方式,并且易于维护,但绝不设计或修改域对象以更兼容或匹配ORM实体。它不是关于更改数据库或ORM,而是将域(以及应用程序的其余部分)与持久性详细信息(ORM所在)正确分离。

因此,要抵制重用其他层的实现细节的诱惑。应用程序在层中构造的原因是因为您需要解耦。保持这种状态。

答案 1 :(得分:18)

为什么不直接使用EF实体作为域对象,因为您正在考虑首先使用 Entity Framework 6代码?首先,您设计域模型然后设计数据库结构。

我一直在使用NHibernate并且相信在EF中你也可以指定从DB表到POCO对象的映射规则,尤其是EF6。在EF实体上开发另一个抽象层是一项额外的工作。让ORM负责。

我不同意这篇文章您可能会阅读"Entity Framework 5 with AutoMapper and Repository Pattern"还有很多其他文章,人们只是将EF实体用作域对象

当您开始构建表示层并面对大量特定于UI的视图模型时,

AutoMapper肯定会对您有所帮助。在没有公共setter的情况下,它对于构建anemic models非常有用,对于真正的Domain对象有点无用。

吉米·博加德"The case for two-way mapping in AutoMapper"发了一篇旧帖,他在那里说"There is no two-way mapping because we never need two-way mapping."

  

那么我们使用AutoMapper的目的是什么?我们的五个档案包括:

     
      
  • 从Domain到ViewModel(MVC的强类型视图模型)
  •   
  • 从Domain到EditModel(MVC中表单的强类型视图模型)
  •   
  • 从EditModel到CommandMessages - 从松散类型的EditModel转换为强类型的,分解的消息。一个EditModel   可能会生成六条消息。
  •   
  • 从Domain到ReportModel - 强类型Telerik报告
  •   
  • 从域到EDI模型 - 用于生成EDI报告的展平模型
  •   

答案 2 :(得分:3)

哦,不,我根本不会添加额外的图层。

NHibernate和实体框架代码优先(我使用EF)旨在解决这个确切的问题 - 将域对象映射到关系模型(它们的设计没有相同的约束,所以可能和可能会,是一个不同的形状)。

浪费EF的优秀映射功能并将其替换为其他东西,甚至是AutoMapper,似乎是一种耻辱。

答案 3 :(得分:0)

我自己研究此主题时,发现我认为大多数使用或建议使用EF POCO作为您的域对象的开发人员根本没有DDD的必要性。对于复杂的域逻辑,DDD是推荐的方法,当您具有复杂的域逻辑时,您极不可能对域逻辑和数据存储结构进行1:1映射。的确,在很多情况下,您将拥有结构与数据库设计相似的域对象,但是随着您的域变得越来越复杂,您会发现许多域实体都需要偏离数据的方式。物理存储。如果您发现自己想要使用自动映射器,我也有相同的看法-在这种情况下,您可能也不需要DDD。您的应用程序可能很简单,不会从DDD的复杂性中受益。提醒您,DDD不是架构或设计模式,它是一种构建软件以使用通用语言开发丰富域模型的方法。

我在企业级系统上工作,一个示例是我们的文档业务逻辑。要存在一个文档,它必须满足以下要求:

  • 分配给标签
  • 根据标签的默认设置分配默认属性
  • 选项卡可能存在或不存在,并且选项卡还具有全局定义的默认属性
  • 文档可能有图像(文件),或者可能没有图像
  • 我们的系统具有模块(跟踪与成像),因此客户甚至可能无法访问存储文件
  • 必须进行审核记录
  • 必须跟踪文档历史记录
  • 更多规则

因此,文档的简单概念不仅涉及大量数据的数据库表,而且创建该文档时会发生许多业务逻辑。没有1:1映射会发生,并且由于我们的系统还具有必须分开的功能模块,因此通过继承和装饰器模式,我们可以拥有不同类型的Document业务实体。

我们也有一个包含约200个数据库表的数据库。我们必须维护一个非常干净和优化的数据库。不可能基于数据库做出业务逻辑决策,反之亦然。它们必须是分开的,以便我们可以根据自己的需要维护各自的状态。域实体需要提供有意义的丰富域模型,并且数据库必须得到优化,正确和快速。

您面临的挑战是一个复杂的挑战-不幸的是,构建软件并不是一项微不足道的任务,不幸的是,业务逻辑和“数据”确实是同一个挑战-如果没有这一点,您将无法拥有一个挑战。其他。碰巧的是,要真正构建软件,我们必须以一种有意义的方式来处理它,既要按功能进行扩展,又要按性能进行扩展。

所有这些所面临的挑战是软件具有如此众多的需求。那么,您如何拥有一个丰富的域模型,使用像EF这样的ORM,又能够以不适合您域模型的方式解决诸如查询数据之类的问题?以及如何以一种不会弄乱代码库的方式来做到这一点?

对于我来说,使用EF之类的东西很大,这意味着找到一种方法,可以轻松地从数据(EF实体)创建域对象,同时支持工作模式,上下文,变更跟踪等单位。对我来说,这看起来像是一个域对象,可以在工作单元中访问由上下文跟踪的实体。这意味着您可以使用一种方法将单个实体加载到域对象中,或者在单个查询表达式中加载多个实体以提高性能,并且仍然使用EF进行更改跟踪以进行插入和更新。

我第一个解决此问题的方法实际上是使用Active Record,这与EF很自然。每个域实体都有其自己的上下文,并管理用于创建,删除和更新的所有业务逻辑。对于90%的软件来说,这是很棒的,并且对性能没有影响。对于高级查询方案,我们将域服务与自己的上下文一起使用。我无法在此描述我们的整个方法,因为它需要一个自己的博客,但是...

我现在也在研究一种不使用Active Record的方法。我认为存储库是关键,能够加载许多存储库并处理查询方案以及规范。

不过,我的观点是,我相信,如果您确实需要DDD,则数据层和域层应该完全分开,并且如果您不这样认为,则可能不需要DDD。