(此文章同时发表在本人微信公众号“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. Casual Note

    20170104 冯诺依曼计算机(遵循冯诺依曼结构设计的计算机:存储器.运算器.控制器.输入设备.输出设备)之前也有计算机,不过在那之前的计算机是专用的,不可编程,只能干特定的事情没法干其他事.与之前 ...

  2. wamp 局域网访问

    1.关闭防火墙 2.设置apache 的httpd.conf 第278行 正确代码如下 # onlineoffline tag - don't remove Require all granted O ...

  3. Struts2 拦截器配置以及实现

    @(Java ThirdParty)[Struts|Interceptor] Struts2 拦截器配置以及实现 Struts2的拦截器应用于Action,可以在执行Action的方法之前,之后或者两 ...

  4. iOS 动态化

    来自bang's blog http://blog.cnbang.net/tech/3286/ 问题 在开发模式上,web 的方式是比较先进的,有各种优点,包括跨平台/UI开发效率高,最重要的是可以时 ...

  5. c语言一些知识点的记录

    1.extern关键字 extern关键字可以置于变量或者函数前面,用于告诉编译器此变量或函数定义于其他的模块.

  6. Could not find or load main class org.gradle.wrapper.GradleWrapperMain解决办法

    解决办法: gradlew is the gradle wrapper executable - batch script on windows and shell script elsewhere. ...

  7. mySql 远程连接(is not allowed to connect to this MySQL server)

    如果你想连接你的mysql的时候发生这个错误: ERROR 1130: Host '192.168.1.3' is not allowed to connect to this MySQL serve ...

  8. R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)

    包含以下几个小的知识点 1htmlTreeParse函数源码和一些参数 2hander的写法 3关于missing函数 4关于if-else语句中else语句的花括号问题 5关于checkHandle ...

  9. GMOLO平板——如何安装新系统

    1.首先,此款平板采用intel处理器Z3735F,不支持Win7,XP,经过测试,Win8可以在此类笔记本及平板上运行,并非只能安装最新的Win10系统. 2.下载Win8 32位系统作为预备(GH ...

  10. Id.value与document.getElementById("Id").value的区别

    如果标签Id在Form表单里面的话,直接Id.value就不能用了,而是要用Form.Id.value来取值或设置值 所以最好用document.getElementById("Id&quo ...