DDD 领域驱动设计-三个问题思考实体和值对象
消息场景:用户 A 发送一个消息给用户 B,用户 B 回复一个消息给用户 A。。。
现有设计:消息设计为实体并为聚合根,发件人、收件人设计为值对象。
三个问题:
- 实体最重要的特性是什么?
- Message 实体是怎么得来的?
- 发件人、收件人为什么不是实体?
1. 实体最重要的特性是什么?
《领域驱动设计》5.2 实体:
摘录一段:许多对象不是由它们的属性来定义,而是通过一系列的连续性(continuity)和标识(identity)来从根本上定义的。
归纳:
- 标识(identity)
- 连续性(continuity)
标识在实体中的另一种体现就是唯一和不可变,其概念在很多资料中有说明,这也是实体最重要的特性。
我有一个双胞胎哥哥,我们俩出生的时候,长得一模一样,以至于我们的爸妈都分不清,不得已他们在我们脖子上系个项链来标记:谁是老大?谁是老二?其实这个“标记”就可以看作是实体的标识,只不过是用项链来标识的,就像我们在项目中使用 GUID 方式一样,目的就是用来体现标识,但不管用什么方式表示,这个标识必须在这个特定环境下唯一,也就是说,我和我双胞胎哥哥的项链不能完全一样,要不然我爸妈就不能区分我们俩了。
我和我那双胞胎哥哥就这样一天一天的长大,但出奇的是,我们哥俩越长越像,以至于我们互相看对方,都以为自己在“照镜子”一样,但唯一不变的是我们俩脖子上的项链,这也是区分我们哥俩的唯一方式。刚出生的我和现在的我,脖子上的项链是一样的,这也就是实体标识的不可变性,也就是说刚出生的我和现在的我是同一个人,项链只不过在我成长的过程中起到“标记”的作用(当然也可以是手带、脚环之类的信物),它会“陪伴”我的一生,这个“陪伴”的过程,可以理解为实体的另一种特性-连续性。
有一天,我们镇要统计双胞胎的分布情况,然后调查人员来到我们家,问我们爸妈:“你们家里有没有双胞胎?几对双胞胎?龙凤胎?还是。。。”,然后我爸妈就报上:“一对双胞胎-两个小子”,然后调查人员就做了笔记走了。在这个过程中,他们丝毫没有提及我脖子上的“项链”,虽然它在我爸妈眼里是那么重要(用来标记我们哥俩),但在调查人员眼里却什么都不是,他们只需要知道我和我双胞胎哥哥是什么样的双胞胎就行了,这也就是实体和值对象的根本区别:实体不仅需要知道它是什么?而且还需要知道它是哪个?而值对象只需要知道它是什么?
特定环境下,实体和值对象的区分例子有很多,比如《领域驱动设计》书中所说的“体育场座位例子”和“ Custorm-Address 例子”等等,但大部分都是强调实体的标识特性,却很少提及连续性,那什么是连续性?这部分内容,在《领域驱动设计》中5.2实体章节中最后部分有提及,但都是零碎的概念性文字,如果不注意的话,很容易会被忽略掉。
摘录一段:只要一个对象在生命周期中能够保持连续性,并且独立于它的属性(即使这些属性对系统用户非常重要),那它就是一个实体。
这个内容可以结合上面我和我双胞胎哥哥的例子进行理解,“项链”会陪伴的我一生,这段话可以拆分对应理解:项链-标识、一生-生命周期、陪伴-连续性。也就是说连续性不能理解为生命周期,它应该理解为:标识在实体生命周期内体现出连续性。
2. Message 实体是怎么得来的?
结合上面实体特性的理解,Message 实体是怎么得来的,就很好理解了,消息场景毫无疑问聚合的是消息,消息实体是怎么得来的?可以换个角度理解:为什么把消息设计为实体?首先看下消息实体符不符合实体的两个特性。
标识(identity):消息场景中消息的区分通过什么?标题?内容?这些都不行,为了保证消息的唯一性,必须使用标识进行区分,而且必须不可变。消息场景中,有可能会出现标题和内容一样的消息,但这却不是同一个消息,就像我和我那双胞胎哥哥,长的一样,却不是同一人,可以这样说:标识的作用就是为了区分,而消息也必须要区分,所以。。。
连续性(continuity):一次我们家吃饭的时候,我一不小心把饭碗给打碎了,然后我妈就痛打了我一顿,她有个做笔记的习惯,记录我们哥俩的日常生活,比如这次需要记录一下:今天打了谁?但当时她打完我之后,却不记得是打了我?还是我哥?然后她就挨个看我们的屁股和项链,来确定今天打了谁?这就是标识在生命周期中连续性的部分体现。消息场景中,在某一阶段需要对消息进行处理,这个处理需要通过标识来明确处理的是哪条消息?这个对消息处理过程的体现就是连续性,有时候连续性需要在标识明确的情况下,但还有一种是其自身的生命周期连续性,比如从消息的创建,到管理,再到最后的销毁,这个过程就是消息实体的连续。
上面的分析说明消息实体符合实体的两个特性,也就是说消息可以设计为实体,至于怎么得来的?可以这样理解,消息场景首先考虑的是消息,就像我们家的双胞胎,首先考虑的是我和我那双胞胎哥哥。
3. 发件人、收件人为什么不是实体?
在之前的一篇博文中,园友鼻涕成诗有这样的疑问:联系人作为值对象这一点有点不太理解,好处是什么?我当时是这样回复的:
联系人作为值对象,因为他不在消息系统中存储,是从外部获取的,而且它的存在要依附于消息,在消息系统这个业务场景中,如果脱离了消息,它就没有什么意义,对于消息而言,我只要知道这个联系人的内容是什么就行了,而不需要它具体什么哪个,人?还是邮箱?这个它并不关心,不是说把联系人作为值对象有什么好处,而是在这个业务场景下,这样设计比较合理些。
回复内容现在看来有些牵强,先不讨论对与错,按照上面消息实体的分析模式,在消息场景下,看下发件人、收件人(可以统称为联系人,发件人和收件人有可能为同一联系人)是否具有实体的一些特性。
标识(identity):联系人是否具有标识?也就是说联系人需不需要进行区分?答案当然是要进行区分,要不然收件箱、发件箱就没办法针对收件人、发件人进行标识,而且联系人有可能名称相同,但是两个不同的联系人,也就是说在消息的整个应用场景中,联系人是必须要唯一标识的,不管它扮演的角色是发件人,还是收件人,这个“角色扮演”概念只是针对某一具体消息来说,联系人所存在的意义(在这个消息中,这个联系人是发件人,但在另外一个消息中,有可能是收件人),但相对于整个消息场景,这个联系人标识是唯一的,而且是不可变的。
连续性(continuity):这个可能没有消息实体的连续性好理解,联系人的连续性其实是依附于消息实体而言,它如果独立出来,自身在消息场景中,是没有连续性概念的,就比如在创建消息的时候,我需要判断收件人是否存在,存在的话就创建收件人对象,并赋予创建消息的收件人属性,还有就是消息在被阅读的时候,需要判断阅读人是否有阅读权限等等,这一些操作,就体现出联系人的连续性依附于消息实体,但不可否认,联系人的创建、使用、舍弃等操作,都可以理解围绕某一具体消息的生命周期,也就是联系人的连续性,而且在这个过程中,联系人的标识都需要首先被明确。
在之前的理解中,联系人设计为值对象的想法是,把联系人看作是一个值,一个依附于消息实体的具体值,我只需要知道这个值就行了,具体体现就是 SenderID 或 RecipientID,其实这个就是联系人的标识,只是当时被两点所迷惑:
- 联系人外部存储:在消息场景中,联系人的获取是从外部获得的,也就是说联系人不在消息场景中存储,也不进行管理,只是一个获取操作,这个和一般的实体场景不太一样,但仔细一想,不管它是从哪里获取的,这个不应该在消息场景中所关心,我应该专注于联系人在消息场景中的连续性。
- 联系人依附于消息:这个是最重要的迷惑点,或者说是我根本不了解实体和值对象到底应该是什么?联系人独立于消息,在消息场景中,没有任何意义,但不能因为这一点,就把它设计为值对象,有很多实体是依附关系,只要它存在标识和连续性,那它就是实体。
把联系人设计为值对象当然也有“好处”,比如可以减少对联系人的管理,因为如果联系人设计为值对象,那它就是一个值,也就没有对象的概念,但出来混的迟早是要还的,我要加一个用户禁言功能,这个在现有的设计中就不好进行实现。像这种依附性实体的场景也很多,比如购物车应用中的 Order 和 Custorm,Custorm 依附于 Order,这个首先需要明确的是购物车应用场景,如果是其他的场景下,那 Custorm 就不存在依附关系。
我和我双胞胎哥哥出生的时候,在我们的保温箱上,除了需要标明我们两个的”身份“之外,还需要标明我们爸妈的”身份“,具体标识可以用身份证号,这个就像消息实体中的 SenderID、RecipientID 一样,虽然它是一个”值“,但我还需要知道它具体标识的是哪个对象,因为我不仅需要它表示的值是多少,我还需要知道它所代表的对象是哪个,就比如我和我双胞胎哥哥要根据这个身份证号,找到我们的父母一样。
4. 发件人、收件人是值对象?还是实体?
话不言多,总之一句话:发件人、收件人(联系人)需要设计为实体。
消息场景实体和值对象:
- Message 消息实体和 Contact 联系人实体。
- 值对象若干(如 MessageState、MessageType 等)。
概念理解:
- What’s the Single Responsibility of an Entity in Domain Driven Design?
- 领域模型-谈实体对象和值对象
- DDD领域驱动设计基本理论知识总结
- 领域驱动设计实现之路
DDD 领域驱动设计-三个问题思考实体和值对象的更多相关文章
- DDD 领域驱动设计-“臆想”中的实体和值对象
其他博文: DDD 领域驱动设计-三个问题思考实体和值对象 DDD 领域驱动设计-三个问题思考实体和值对象(续) 以下内容属于博主"臆想",如有不当,请别当真. 扯淡开始: 诺兰的 ...
- DDD 领域驱动设计-三个问题思考实体和值对象(续)
上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...
- C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)
前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...
- DDD领域驱动设计初探(三):仓储Repository(下)
前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...
- DDD 领域驱动设计-如何完善 Domain Model(领域模型)?
上一篇:<DDD 领域驱动设计-如何 DDD?> 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sample(代码已更新) 阅读目录: ...
- DDD 领域驱动设计-看我如何应对业务需求变化,领域模型调整?
写在前面 上一篇:DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对? "愚蠢的应对",这个标题是我后来补充上的,博文中除了描述需求变化.愚蠢应对和一些思考,确实没有实质性 ...
- C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)
前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...
- DDD领域驱动设计仓储Repository
DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repositor ...
- 关于DDD领域驱动设计的理论知识收集汇总
原文:关于DDD领域驱动设计的理论知识收集汇总 最近一直在学习领域驱动设计(DDD)的理论知识,从网上搜集了一些个人认为比较有价值的东西,贴出来和大家分享一下: 我一直觉得不要盲目相信权威,比如不能一 ...
随机推荐
- (转)什么是“黑客” by 王珢
什么是“黑客” by 王垠很多程序员自豪的把自己叫做“黑客”(hacker),把编程叫做 hack.可是殊不知,其实在最高级的程序员眼里,“黑客”其实是一个贬义词.他们更愿意被叫做“程序员”(prog ...
- div样式text-align在子元素缩进不规范的情况下,chrome出现的问题(貌似结果是inline-block导致的)
在调css的时候,发现居然有firefox实现效果完整而chrome出现问题的情况: 详细情况:segmentfault1 segmentfault2 全文表达: <html><he ...
- 我的window10
前言 这个一时半会写不完,也比较耗费时间,留着以后,每周更新一些新的技巧. 折腾了3天多时间的成果——>window10 的全新桌面,不比苹果差!不要说 windows 不能用 mac . 既然 ...
- Win10 下安装 NodeJS
1,右键点击底部导航栏win(开始)弹出,使用 命令提示符(管理员A) 2,输入命令,进入安装文件目录,输入 msiexec/package node-v4.4.4-x64.msi ----弹出安 ...
- Android再学习
1.点击事件的几种实现方式 Button Btn1 = (Button)findViewById(R.id.button1);//获取按钮资源 Btn1.setOnClickListener(new ...
- poj2718-Smallest Difference(枚举全排列)
一,题意: 给出最多10个数字,将它们划分为两个整数,求差值最小的值(除非只有一位数,否则不允许出现先导0) 很显然如果总共有n个数,必然有一个整数长n/2,另一个长n-n/2.二,思路: 利用nex ...
- ASP.Net MVC开发基础学习笔记:二、HtmlHelper与扩展方法
一.一个功能强大的页面开发辅助类—HtmlHelper初步了解 1.1 有失必有得 在ASP.Net MVC中微软并没有提供类似服务器端控件那种开发方式,毕竟微软的MVC就是传统的请求处理响应的回归. ...
- Meteor+AngularJS:超快速Web开发
为了更好地描述Meteor和AngularJS为什么值得一谈,我先从个人角度来回顾一下这三年来WEB开发的变化: 三年前,我已经开始尝试前后端分离,后端使用php的轻量业务逻辑框架.但 ...
- 现代程序设计 网页前端开发作业(to 邹欣老师)
在一些著名的网站的搜索框上,会有一种“自动完成”功能. 比如google.百度和淘宝: 现在,我们来考虑如何实现这个功能. 第一步:模仿任意一个网站,编写一个类似的网页效果原型. 在这一步,不要估计开 ...
- [翻译] AKKA笔记- ACTORSYSTEM (配置CONFIGURATION 与调度SCHEDULING) - 4(一)
原文在http://rerun.me/2014/10/06/akka-notes-actorsystem-in-progress/ 像我们前面看到的,我们可以用ActorSystem的actorof方 ...