(此文章同时发表在本人微信公众号“dotNET每日精华文章”,欢迎右边二维码来关注。)

题记:最近在ABP项目中尝试纯粹的DDD,然后遇到EF实现的Repository似乎不能很好支持DDD,但……可能是我们搞错了。

ABP即ASP.NET Boilerplate,一个融合了很多最佳实践(比如领域驱动设计,Domain Driven Design,DDD)的应用程序开发框架。当然,ABP并没有强制要求你严格使用DDD中的概念来开发。换句话说,也可以很好的支持DDD的概念,比如仓储(Repository)的概念(ABP分别提供了Entity Framework和NHibernate的实现)。

ABP在刚刚更新的0.13.0版本中引入了名为AggregateRoot的基类,可以让你方便的引入DDD中的聚合和聚合根的概念了。

不过我在基于聚合来实现功能的时候,遇到了一个问题:在EF的Repository实现下,聚合中的实体无法通过聚合根来正确删除。举例来说,假设聚合中包含Order和OrderLine两个实体,Order是聚合根,那么如果通过

Order.OrderLines.Remove(orderLine)

这样的代码来删除OrderLine数据的话,就会报错。类似的问题很多人其实也遇到过,比如这篇文章就详细解释了这种情况:http://blog.raffaeu.com/archive/2014/10/04/entity-framework-6-and-collections-with-ddd.aspx。当然在stackoverflow上也有很多这样的提问。并且这个问题在2010年就被人在UserVoice提出来了,而微软一直都没有进行改变。

一开始,我也认为这是EF存在的缺陷。但是当我再次回顾聚合和聚合根中的相关特点的时候(以下文字引用于汤雪华的博文):

  • 每个聚合有一个根和一个边界,边界定义了一个聚合内部有哪些实体或值对象,根是聚合内的某个实体;

  • 聚合内部的对象之间可以相互引用,但是聚合外部如果要访问聚合内部的对象时,必须通过聚合根开始导航,绝对不能绕过聚合根直接访问聚合内的对象,也就是说聚合根是外部可以保持 对它的引用的唯一元素;

  • 聚合内除根以外的其他实体的唯一标识都是本地标识,也就是只要在聚合内部保持唯一即可,因为它们总是从属于这个聚合的;

  • 聚合根负责与外部其他对象打交道并维护自己内部的业务规则;

  • 基于聚合的以上概念,我们可以推论出从数据库查询时的单元也是以聚合为一个单元,也就是说我们不能直接查询聚合内部的某个非根的对象;

  • 聚合内部的对象可以保持对其他聚合根的引用;

  • 删除一个聚合根时必须同时删除该聚合内的所有相关对象,因为他们都同属于一个聚合,是一个完整的概念;

同时也找出了一个被我们常常忽视的EF特点:标识和非标识关系的区别。所谓标识关系就是:主实体(即Order)的主键除了作为依赖实体(即OrderLine)的外键也要成为依赖实体的主键的一部分(即需要混合主键)。非标识关系就是:主实体的主键只作为依赖实体的外键。两种关系的最大不同就在于:标识关系下,依赖实体不能独立于主实体存在,删除主实体也会删除依赖实体(效果类似于级联删除),删除关系就会删除依赖实体。对于这一特点的详细说明可见此MSDN文档的"Considerations for Identifying and Non-identifying Relationships”。

结合DDD聚合的特点“聚合内除根以外的其他实体的唯一标识都是本地标识,也就是只要在聚合内部保持唯一即可”,以及上面提到的EF标识关系的特点“删除关系就会删除依赖实体”。我们可能错怪EF了,正确做法是:应该把聚合中的非根实体设置为标识关系。这样的建模方式即体现了OrderLine的ID是本地标识的特性(要全局唯一必须混合Order的ID),又满足了通过聚合根来删除明细实体的目的。

其实这是一个很细的设计和实现问题,很多人有遇到,当然在stackoverflow也有人给出了解决方案。我这里只是给出了一点自己的思考,意欲解释一下这个问题出现的根源和解决方案背后的理论基础。

EF不能很好的支持DDD?估计是我们搞错了!的更多相关文章

  1. DDD实战进阶第一波(四):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架三)

    上一篇文章我们讲了经典DDD架构对比传统三层架构的优势,以及经典DDD架构每一层的职责后,本篇文章将介绍基础结构层中支持DDD的轻量级框架的主要代码. 这里需要说明的是,DDD轻量级框架能够体现DDD ...

  2. DDD实战进阶第一波(二):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架一)

    要实现软件设计.软件开发在一个统一的思想.统一的节奏下进行,就应该有一个轻量级的框架对开发过程与代码编写做一定的约束. 虽然DDD是一个软件开发的方法,而不是具体的技术或框架,但拥有一个轻量级的框架仍 ...

  3. DDD实战进阶第一波(三):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架二)

    了解了DDD的好处与基本的核心组件后,我们先不急着进入支持DDD思想的轻量级框架开发,也不急于直销系统需求分析和具体代码实现,我们还少一块, 那就是经典DDD的架构,只有了解了经典DDD的架构,你才能 ...

  4. python列表很聪明,支持负数索引

    python列表很聪明,支持负数索引

  5. 一个很不错的支持Ext JS 4的上传按钮

    以前经常使用的swfUpload,自从2010年开始到现在,很久没更新了.而这几年,flash版本已经换了好多个,所以决定抛弃swfupload,使用新找到的上传按钮. 新的上传按钮由harrydel ...

  6. ef查询mysql数据库数据支持DbFunctions函数

    1.缘由 快下班的时候,一同事说在写linq查询语句时where条件中写两时间相减大于某具体天数报错:后来仔细一问,经抽象简化,可以总结为下面的公式: a.当前时间 减去 某表时间字段 大于 某具体天 ...

  7. 有了 Docker,用 JavaScript 框架开发的 Web 站点也能很好地支持网络爬虫的内容抓取

    点这里 阅读目录 用 AngularJS(以及其它 JavaScript 框架)开发的 Web 站点不支持爬虫的抓取 解决方案 为什么公开我们的解决方案 实现 AngularJS 服务 结论   Pr ...

  8. 实体框架 (EF) 入门 => 三、CodeFirst 支持的完整特性列表

    KeyAttribute 设置主键.如果为int类型,将自动设置为自增长列. 系统默认以Id或类名+Id作为主键.StringLengthAttribute 可设置最大最小长度以及验证提示信息等.最大 ...

  9. EF Core 2.0 已经支持自动生成父子关系表的实体

    现在我们在SQL Server数据库中有Person表如下: CREATE TABLE [dbo].[Person]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Cr ...

随机推荐

  1. express源码剖析1

    在通读源码之前,先把一些比较难理解的代码吃透: 1.EventEmitter.prototype mixin(app, EventEmitter.prototype, false); app为一个函数 ...

  2. mysql ,服务器耗时测试

    测试一 字段:ID,test,number 电脑配置:8g内存 同时写入超过5w条,就会卡死 错误提示 Fatal error: Allowed memory size of bytes exhaus ...

  3. Windows下LATEX排版论文攻略—CTeX、JabRef使用介绍

    Windows下LATEX排版论文攻略—CTeX.JabRef使用介绍 一.工具介绍 TeX是一个很好排版工具,在学术界十分流行,特别是数学.物理学和计算机科学界. CTeX是TeX中的一个版本,指的 ...

  4. 显示oracle表的分区信息

    显示分区表信息 显示数据库所有分区表的信息:DBA_PART_TABLES 显示当前用户可访问的所有分区表信息:ALL_PART_TABLES 显示当前用户所有分区表的信息:USER_PART_TAB ...

  5. Linux htop工具使用详解

    一.Htop的使用简介 大家可能对top监控软件比较熟悉,今天我为大家介绍另外一个监控软件Htop,姑且称之为top的增强版,相比top其有着很多自身的优势.如下: 两者相比起来,top比较繁琐 默认 ...

  6. 分分钟教你从根本上认识Struts2框架

    在了解Struts2之前我们先来聊聊Struts1,我们都知道在很长的一段时间内,所有的MVC框架中,Struts1他是处于一个超级大咖的地位,无论是从市场角度和使用的用户的数量这个角度而言,Stru ...

  7. css兼容性大坑

    一. \:选择IE6+//区分 IE 8(不实用) .title{ color:yellow\0; color: red\9\0;} \9在 IE 6及其以上都可以识别(但是 IE11不识别 ,IE ...

  8. webstorm ES6 转 ES5

    一句话总结:用WebStorm自带的File Watcher功能+Babel实现自动转换ECMAScript 6代码为ES5代码 1. 新建一个Empty Project,然后在src目录下新建了一个 ...

  9. 第三篇 基于.net搭建热插拔式web框架(重造Controller)

    由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并且http上下文不支持跨域,所以我们要重造一个contro ...

  10. qlikview 扩展插件制作教程-EchartsGeoMap

    效果图   显示效果和echarts官方demo一样,运行速度尚可. 第一次写博客,排版很渣以后慢慢改进. 基础知识 以EchartsGeoMap为例,讲一下怎么制作一个基础的QlikView Ext ...