消息场景:用户 A 发送一个消息给用户 B,用户 B 回复一个消息给用户 A。。。

现有设计:消息设计为实体并为聚合根,发件人、收件人设计为值对象。

三个问题:

  1. 实体最重要的特性是什么?
  2. Message 实体是怎么得来的?
  3. 发件人、收件人为什么不是实体?

1. 实体最重要的特性是什么?

《领域驱动设计》5.2 实体:

摘录一段:许多对象不是由它们的属性来定义,而是通过一系列的连续性(continuity)和标识(identity)来从根本上定义的。

归纳:

  • 标识(identity)
  • 连续性(continuity)

标识在实体中的另一种体现就是唯一不可变,其概念在很多资料中有说明,这也是实体最重要的特性。

我有一个双胞胎哥哥,我们俩出生的时候,长得一模一样,以至于我们的爸妈都分不清,不得已他们在我们脖子上系个项链来标记:谁是老大?谁是老二?其实这个“标记”就可以看作是实体的标识,只不过是用项链来标识的,就像我们在项目中使用 GUID 方式一样,目的就是用来体现标识,但不管用什么方式表示,这个标识必须在这个特定环境下唯一,也就是说,我和我双胞胎哥哥的项链不能完全一样,要不然我爸妈就不能区分我们俩了。

我和我那双胞胎哥哥就这样一天一天的长大,但出奇的是,我们哥俩越长越像,以至于我们互相看对方,都以为自己在“照镜子”一样,但唯一不变的是我们俩脖子上的项链,这也是区分我们哥俩的唯一方式。刚出生的我和现在的我,脖子上的项链是一样的,这也就是实体标识的不可变性,也就是说刚出生的我和现在的我是同一个人,项链只不过在我成长的过程中起到“标记”的作用(当然也可以是手带、脚环之类的信物),它会“陪伴”我的一生,这个“陪伴”的过程,可以理解为实体的另一种特性-连续性

有一天,我们镇要统计双胞胎的分布情况,然后调查人员来到我们家,问我们爸妈:“你们家里有没有双胞胎?几对双胞胎?龙凤胎?还是。。。”,然后我爸妈就报上:“一对双胞胎-两个小子”,然后调查人员就做了笔记走了。在这个过程中,他们丝毫没有提及我脖子上的“项链”,虽然它在我爸妈眼里是那么重要(用来标记我们哥俩),但在调查人员眼里却什么都不是,他们只需要知道我和我双胞胎哥哥是什么样的双胞胎就行了,这也就是实体和值对象的根本区别:实体不仅需要知道它是什么?而且还需要知道它是哪个?而值对象只需要知道它是什么?

特定环境下,实体和值对象的区分例子有很多,比如《领域驱动设计》书中所说的“体育场座位例子”和“ Custorm-Address 例子”等等,但大部分都是强调实体的标识特性,却很少提及连续性,那什么是连续性?这部分内容,在《领域驱动设计》中5.2实体章节中最后部分有提及,但都是零碎的概念性文字,如果不注意的话,很容易会被忽略掉。

摘录一段:只要一个对象在生命周期中能够保持连续性,并且独立于它的属性(即使这些属性对系统用户非常重要),那它就是一个实体。

这个内容可以结合上面我和我双胞胎哥哥的例子进行理解,“项链”会陪伴的我一生,这段话可以拆分对应理解:项链-标识、一生-生命周期、陪伴-连续性。也就是说连续性不能理解为生命周期,它应该理解为:标识在实体生命周期内体现出连续性

2. Message 实体是怎么得来的?

结合上面实体特性的理解,Message 实体是怎么得来的,就很好理解了,消息场景毫无疑问聚合的是消息,消息实体是怎么得来的?可以换个角度理解:为什么把消息设计为实体?首先看下消息实体符不符合实体的两个特性。

  • 标识(identity):消息场景中消息的区分通过什么?标题?内容?这些都不行,为了保证消息的唯一性,必须使用标识进行区分,而且必须不可变。消息场景中,有可能会出现标题和内容一样的消息,但这却不是同一个消息,就像我和我那双胞胎哥哥,长的一样,却不是同一人,可以这样说:标识的作用就是为了区分,而消息也必须要区分,所以。。。

  • 连续性(continuity):一次我们家吃饭的时候,我一不小心把饭碗给打碎了,然后我妈就痛打了我一顿,她有个做笔记的习惯,记录我们哥俩的日常生活,比如这次需要记录一下:今天打了谁?但当时她打完我之后,却不记得是打了我?还是我哥?然后她就挨个看我们的屁股项链,来确定今天打了谁?这就是标识在生命周期中连续性的部分体现。消息场景中,在某一阶段需要对消息进行处理,这个处理需要通过标识来明确处理的是哪条消息?这个对消息处理过程的体现就是连续性,有时候连续性需要在标识明确的情况下,但还有一种是其自身的生命周期连续性,比如从消息的创建,到管理,再到最后的销毁,这个过程就是消息实体的连续。

上面的分析说明消息实体符合实体的两个特性,也就是说消息可以设计为实体,至于怎么得来的?可以这样理解,消息场景首先考虑的是消息,就像我们家的双胞胎,首先考虑的是我和我那双胞胎哥哥。

3. 发件人、收件人为什么不是实体?

在之前的一篇博文中,园友鼻涕成诗有这样的疑问:联系人作为值对象这一点有点不太理解,好处是什么?我当时是这样回复的:

联系人作为值对象,因为他不在消息系统中存储,是从外部获取的,而且它的存在要依附于消息,在消息系统这个业务场景中,如果脱离了消息,它就没有什么意义,对于消息而言,我只要知道这个联系人的内容是什么就行了,而不需要它具体什么哪个,人?还是邮箱?这个它并不关心,不是说把联系人作为值对象有什么好处,而是在这个业务场景下,这样设计比较合理些。

回复内容现在看来有些牵强,先不讨论对与错,按照上面消息实体的分析模式,在消息场景下,看下发件人、收件人(可以统称为联系人,发件人和收件人有可能为同一联系人)是否具有实体的一些特性。

  • 标识(identity):联系人是否具有标识?也就是说联系人需不需要进行区分?答案当然是要进行区分,要不然收件箱、发件箱就没办法针对收件人、发件人进行标识,而且联系人有可能名称相同,但是两个不同的联系人,也就是说在消息的整个应用场景中,联系人是必须要唯一标识的,不管它扮演的角色是发件人,还是收件人,这个“角色扮演”概念只是针对某一具体消息来说,联系人所存在的意义(在这个消息中,这个联系人是发件人,但在另外一个消息中,有可能是收件人),但相对于整个消息场景,这个联系人标识是唯一的,而且是不可变的

  • 连续性(continuity):这个可能没有消息实体的连续性好理解,联系人的连续性其实是依附于消息实体而言,它如果独立出来,自身在消息场景中,是没有连续性概念的,就比如在创建消息的时候,我需要判断收件人是否存在,存在的话就创建收件人对象,并赋予创建消息的收件人属性,还有就是消息在被阅读的时候,需要判断阅读人是否有阅读权限等等,这一些操作,就体现出联系人的连续性依附于消息实体,但不可否认,联系人的创建、使用、舍弃等操作,都可以理解围绕某一具体消息的生命周期,也就是联系人的连续性,而且在这个过程中,联系人的标识都需要首先被明确

在之前的理解中,联系人设计为值对象的想法是,把联系人看作是一个值,一个依附于消息实体的具体值,我只需要知道这个值就行了,具体体现就是 SenderID 或 RecipientID,其实这个就是联系人的标识,只是当时被两点所迷惑:

  1. 联系人外部存储:在消息场景中,联系人的获取是从外部获得的,也就是说联系人不在消息场景中存储,也不进行管理,只是一个获取操作,这个和一般的实体场景不太一样,但仔细一想,不管它是从哪里获取的,这个不应该在消息场景中所关心,我应该专注于联系人在消息场景中的连续性。
  2. 联系人依附于消息:这个是最重要的迷惑点,或者说是我根本不了解实体和值对象到底应该是什么?联系人独立于消息,在消息场景中,没有任何意义,但不能因为这一点,就把它设计为值对象,有很多实体是依附关系,只要它存在标识和连续性,那它就是实体。

把联系人设计为值对象当然也有“好处”,比如可以减少对联系人的管理,因为如果联系人设计为值对象,那它就是一个值,也就没有对象的概念,但出来混的迟早是要还的,我要加一个用户禁言功能,这个在现有的设计中就不好进行实现。像这种依附性实体的场景也很多,比如购物车应用中的 Order 和 Custorm,Custorm 依附于 Order,这个首先需要明确的是购物车应用场景,如果是其他的场景下,那 Custorm 就不存在依附关系。

我和我双胞胎哥哥出生的时候,在我们的保温箱上,除了需要标明我们两个的”身份“之外,还需要标明我们爸妈的”身份“,具体标识可以用身份证号,这个就像消息实体中的 SenderID、RecipientID 一样,虽然它是一个”值“,但我还需要知道它具体标识的是哪个对象,因为我不仅需要它表示的值是多少,我还需要知道它所代表的对象是哪个,就比如我和我双胞胎哥哥要根据这个身份证号,找到我们的父母一样。

4. 发件人、收件人是值对象?还是实体?

话不言多,总之一句话:发件人、收件人(联系人)需要设计为实体。

消息场景实体和值对象:

  • Message 消息实体和 Contact 联系人实体。
  • 值对象若干(如 MessageState、MessageType 等)。

概念理解:

DDD 领域驱动设计-三个问题思考实体和值对象的更多相关文章

  1. DDD 领域驱动设计-“臆想”中的实体和值对象

    其他博文: DDD 领域驱动设计-三个问题思考实体和值对象 DDD 领域驱动设计-三个问题思考实体和值对象(续) 以下内容属于博主"臆想",如有不当,请别当真. 扯淡开始: 诺兰的 ...

  2. DDD 领域驱动设计-三个问题思考实体和值对象(续)

    上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...

  3. C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)

    前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...

  4. DDD领域驱动设计初探(三):仓储Repository(下)

    前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...

  5. DDD 领域驱动设计-如何完善 Domain Model(领域模型)?

    上一篇:<DDD 领域驱动设计-如何 DDD?> 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sample(代码已更新) 阅读目录: ...

  6. DDD 领域驱动设计-看我如何应对业务需求变化,领域模型调整?

    写在前面 上一篇:DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对? "愚蠢的应对",这个标题是我后来补充上的,博文中除了描述需求变化.愚蠢应对和一些思考,确实没有实质性 ...

  7. C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

  8. DDD领域驱动设计仓储Repository

    DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repositor ...

  9. 关于DDD领域驱动设计的理论知识收集汇总

    原文:关于DDD领域驱动设计的理论知识收集汇总 最近一直在学习领域驱动设计(DDD)的理论知识,从网上搜集了一些个人认为比较有价值的东西,贴出来和大家分享一下: 我一直觉得不要盲目相信权威,比如不能一 ...

随机推荐

  1. MVC辅助方法

    我相信很多人虽然经常用mvc去做一些东西,但是可能很少使用mvcHtml辅助方法 首先我们要创建一个mvc项目 二创建一个HtmlHelper文件夹 三编写文件 四调用辅助方法 调用辅助方法 和使用其 ...

  2. 突破瓶颈,对比学习:Eclipse开发环境与VS开发环境的调试对比

    曾经看了不少Java和Android的相关知识,不过光看不练易失忆,所以,还是写点文字,除了加强下记忆,也证明我曾经学过~~~ 突破瓶颈,对比学习: 学习一门语言,开发环境很重,对于VS的方形线条开发 ...

  3. 你的眼睛背叛你的心:解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题

    在我们将站点从 ASP.NET + Windows 迁移至 ASP.NET Core + Linux 的过程中,目前遇到的最大障碍就是 —— 没有可用的支持 .NET Core 的 memcached ...

  4. 【php爬虫】百万级别知乎用户数据爬取与分析

    代码托管地址:https://github.com/hoohack/zhihuSpider 这次抓取了110万的用户数据,数据分析结果如下: 开发前的准备 安装Linux系统(Ubuntu14.04) ...

  5. 修改Hosts为何不生效,是DNS缓存?

    Update: 如果浏览器使用了代理工具,修改 Hosts 也不会生效.这里是因为,浏览器会优先考虑代理工具(如添加 pac 文件.SwitchySharp等)的代理,建议调试的时候先关闭这些代理. ...

  6. iOS开发系列--C语言之基础知识

    概览 当前移动开发的趋势已经势不可挡,这个系列希望浅谈一下个人对IOS开发的一些见解,这个IOS系列计划从几个角度去说IOS开发: C语言 OC基础 IOS开发(iphone/ipad) Swift ...

  7. 建站技能get(1)— Asp.net MVC快速集成ckplayer网页视频播放器

    故事背景大概是这样的,我厂两年前给山西晋城人民政府做了一个门户网站(地址:http://jccq.cn/),运行了一年多固若金汤,duang的有一天市场部门过来说,新闻管理模块带视频的内容播放不了了. ...

  8. CSharpGL(0)一个易学易用的C#版OpenGL

    +BIT祝威+悄悄在此留下版了个权的信说: CSharpGL(0)一个易学易用的C#版OpenGL CSharpGL是我受到SharpGL的启发,在整理了SharpGL,GLM,SharpFont等开 ...

  9. 如何创建一个RESTful WCF Service

    原创地址:http://www.cnblogs.com/jfzhu/p/4044813.html 转载请注明出处 (一)web.config文件 要创建REST WCF Service,endpoin ...

  10. Step by step Dynamics CRM 2013安装

    原创地址:http://www.cnblogs.com/jfzhu/p/4008391.html 转载请注明出处   SQL Server可以与CRM装在同一台计算机上,也可安装在不同的计算机上.演示 ...