写在前面

  实体(Entity)、对象(Object)、DTO(Data Transfer Object)数据传输对象,老生常谈话题,简单的概念,换个角度你会发现更多的东西。个人拙见,勿喜请喷。

实体和值对象

  在常规开发中(事务脚本),我们所说的实体只是一些数据库映射的字段,对象只不过是包含业务功能描述的集合而已,在DDD(领域驱动设计)中,实体(Entity)和值对象(Value Object)是基本元素之一,事务脚本中所说的对象概念大概就是领域模型中领域(Domain)的概念了,但并不是只是业务功能描述的集合而已,不针对功能实现,而是针对业务协作完成的一种流程,DDD中的实体是一种领域对象,区别实体和值对象的方法就是判断是否有唯一标示,而不是属性,即使属性完全相同也可能是两个不同的对象。同时实体本身有状态的,而且有自己的生命周期,实体本身会体现出相关的业务行为,业务行为会对实体属性或状态造成影响和改变。比如双胞胎假设所有的属性都一样,仍然是两个不同的人。从哲学角度讲,这正是实体的本来含义。它是除了所有属性之外还不足以表达的“那个”东西,不依赖其它而自存的东西。

  如何区分实体和值对象,比如在城市社保系统中,参与社保人就是一个实体,在业务系统中,社保是一种概念,针对的是参与社保人,所以我们要在业务中来区分参与社保人,人有可能同名同姓,所以不能用名字来区分,这里的名字就是参与社保人的一个属性,所以我们用身份证号来区分参与社保人,这里的身份证号就是参与社保人的唯一标示,用来说明:实体是什么?实体是哪个?

  还有有一种业务场景是这样,比如在全国社保统计系统中,统计各个城市的参与社保的比率,因为我们只要知道这个人是不是参与了城市社保?而并不需要知道他是哪个人,所以这里面的参与社保人就是一个值对象,只是用来说明:值对象是什么?

  可以看出区分实体和值对象只是在特定的业务场景下,同一种特定对象可能会有不同的方式看待,这里面就是一个边界的问题,而且特定的业务场景中的实体是有自己的状态和生命周期,这和值对象也有明显的区分。

实体和对象

道可道,非常道。名可名,非常名。 无名天地之始,有名万物之母。故常无欲以观其妙; 常有欲以观其徼(jiào)。 此两者同出而异名,同谓之玄,玄之又玄,众妙之门。    --《道德经》

  上面这段话出自老子的道德经的开篇,简单说下前两段话的意思:道似乎有具体的定义,但总不是我们所想象出的定义,名取出了一个名,但不一定我们会一直使用。下面几段就是对有(名)和无(道)的辩证关系,最后得出:“无”是天地的来处,“有”是衍生万物的结果,这两者之间,同出为意义不一样,同样好似玄妙务必,无穷无尽,切是研究一切的门经。

  为什么会引用道德经?其实在我看来,老子不做软件开发真是太亏了(哈哈),什么是实体和对象?可能每个人都有自己的解读,就像上面讨论的实体和值对象,其实某种意义上来说应该是领域对象和对象属性,这里说对象属性也并不是准确,就比如项目中我们使用的属性字典,并不是任何一种对象的属性,只是一个特定的值,不依附于任何对象。实体虽然称作实体,其实是一个对象,值对象虽然称作对象,但其实只是一个特定值,意义就像”道可道,非常道,名可名,非常名“一样,

  老子探讨的有无关系,其实在软件编程中就是实体和对象的关系,“有”可以看做是“实体”,“无”可以看做是“对象”,无衍生出有,有体现出无,就像实体和对象之间的关系,实体是不是对象?对象是不是实体?实体和对象到底什么?这其实只是存在一个边界问题。正如DDD中,实体即是领域对象,对象即是实体模型,我们生活中常常讨论:“是先有的鸡蛋?还是先有的鸡?”最后都没有得出一个准确的结果,如果老子来回答这个问题,就六个字:“鸡生蛋,蛋生鸡”,至于解释,老子挥一挥衣袖,骑上青牛远去-“自己去琢磨吧”。

故常无欲以观其妙,常有欲以观其徼

故常无欲以观其妙,常有欲以观其徼(jiào)。    --《道德经》

  这段话我觉得是道德经开篇的精髓,你可能从字面上可以体会得到一些内容,这其实一种态度,一种生活态度,一种编程态度。

  “故常无欲以观其妙”,这句话在我们的现实生活中可以很好的去解读,世间万物是如此的大,我们还有很多的事物没有去认知,所以我们就会抱着征服的欲望去探寻,看到美好的事物就想去掠夺占有,就像当你偶然发现一朵非常漂亮的鲜花,很多人不会停留下去欣赏它,而是去采摘它,然后据为己有,生活中的例子比比皆是,就像文章会犯错一样。

  老子的所提倡的就是我们应该保持“无欲”的心态去看待事件万物,去观察,去体会它的玄妙,上面所说的掠夺、占有,就不是“观”所蕴含的意义了。我们在做项目中,项目前期需求还没有确定好就去开发,到最后弄得进退两难,在DDD中,业务需求是很重要的一环,我们应该花更多的时间去了解它、体会它、确定它,而不是想当然的了解后就去开发项目,这也是建模专家所必备的基本条件。

  “常有欲以观其徼”,这句话的中的精髓就一个字“徼”,徼翻译为边界的意思,“有欲”在现实生活中可以指一些有名望、有地位、有财富的人,这些人当拥有了一些常人所不能拥有的东西后,并不懂得收敛和满足,反而使自己的欲望心更大,想得到更大的满足,得到后还想得到,没有一个界限,最后的下场一般都是很惨,就像和珅贪得无厌一样。

  老子所提倡的就是我们在“有欲”之后,要观察、体会一个界限,要使自己的“欲”控制在这个界限中,水满则溢就是这个道理。就像在DDD中,实体和值对象边界的确定一样。

初始实体和演化实体

含德之厚,比于赤子。
专气致柔,能如婴儿乎?
为天下豁,常德不离,复归于婴儿。
--《道德经》

  老子在道德经中多次提到有关婴儿的话题,就像上面几句,总是拿一些事物和婴儿进行比较,难道说老子喜欢婴儿?准确的应该说,老子推崇婴儿的那种状态,何种状态?无欲无求、回归自然、保持天性。。。

  人的进化史进行了千百万年,从最初的简单生存原则,发展到现在复杂的人世关系,越进化越复杂,导致我们现在越活越累。新出生的婴儿没有任何外界的掺杂,是如此的纯净,正如一碗清水一般,但随着成长,慢慢的接触外界事物,清水也会被染成五颜六色,而失去了本来固有的一些东西,这也就是为什么老子推崇婴儿的原因。有时候我们离开喧嚣的城市,置身于宁静的山坳,你会发现身心是如此的舒畅,其实这才是我们所固有的东西,只是处在乱世中,把那一抹清明掩盖罢了。

  什么是初始实体和演化实体?这只不过是我自己定义的,这里面的实体也可以看做是对象,只是在DDD中称作为实体,如上面所说,婴儿就像初始实体一样,长大后的我们就是演化实体,初始实体只有一种状态,也就是一种原始状态或者称作是无状态,特定的场景下初始实体只有一个抽象出来的对象,但是演化实体有很多种,但都是从初始实体演化出来的,所以称为演化实体,有直接的关系也有间接的关系,如果把婴儿看做是初始实体,长大后的我们是演化实体,但演化实体并不只有长大后我们,汽车、成绩单、衣服等等一些与我们相关的事物都可以称为演化实体,但长大后的我们只是和初始实体有直接关系,其他的和初始实体都是间接关系,这个特定的场景就是人类进化史。

  当然有人看到这可能有些想法,认为你这说的什么乱七八糟的东西,没有一点实际的意义。我的意思并不是说明初始实体和演化实体是个什么东西,而是说在我们做项目的过程中要找到那个“初始实体”,比如物流业务系统场景,在这个系统中哪个是初始实体?调度?账单?扫描?都不是,准确的说应该是运单,因为所有的业务操作都是围绕它来展开,或者是由它演生而来,虽然初始实体我们找到了,但是要仔细的揣摩它,确定是出生的婴儿还是长大后的我们?

  聚合(Aggregate)和聚合根(Aggregate Root)是DDD中的重要概念,什么是聚合?它通过定义对象之间清晰的所属关系和边界来实现领域模型的内聚,并避免了错综复杂的难以维护的对象关系网的形成,聚合定义了一组具有内聚关系的相关对象的集合,我们把聚合看作是一个修改数据的单元。如果把人类社会看做是领域模型,聚合看做是一个国家,国家中的人是一个实体,那这个国家的人就是一个聚合根,但是每个国家的人都是人,只不过肤色、语言、习俗会有些不同,可以把人看做这个场景中的初始实体,也就是初始聚合根,而不是国家的人。

代码中的DTO

  DTO(Data Transfer Object)数据传输对象,注意关键字“数据”两个字,并不是对象传输对象(Object Transfer Object),所以只是传输数据,并不包含领域业务处理,虽然用途只是传输数据,但本身其实也是对象,完成与领域对象之间的转换,就像上面说的值对象一样,某种意义上DTO可以看做是值对象的集合,只不过是和领域对象之间的映射,不包含任何的业务逻辑。

  为什么要使用DTO?主要原因是隔离Domain Model,使改动领域模型而不影响UI,还有就是保持领域模型的安全,不暴露业务逻辑。还有就是在分布式模式下,不同的场景使用相同的数据结构有不同的需求,而我们又不得不做一些数据转化,这是很繁琐的,如果我们在项目初期,做DTO的分析,这样我们就会省很多的事,而且还不会影响整个项目的业务流程,也方便以后对项目进行扩展。

  下面我们虚拟一个简单“文章”领域模型:

         public class Article : IEntity
{
public Article()
{
this.Id = Guid.NewGuid();
}
public string Title { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public DateTime PostTime { get; set; }
public string Remark { get; set; }
#region IEntity Members
/// <summary>
/// 读取或设置文章的编号
/// </summary>
public Guid Id { get; set; }
#endregion
}

  文章领域模型对应DTO:

         public class ArticleDTO
{
/// <summary>
/// 文章唯一编码
/// </summary>
public string ArticleID { get; set; }
/// <summary>
/// 文章标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 文章摘要
/// </summary>
public string Summary { get; set; }
/// <summary>
/// 文章内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 文章作者
/// </summary>
public string Author { get; set; }
/// <summary>
/// 文章发表日期
/// </summary>
public DateTime PostTime { get; set; }
/// <summary>
/// 文章发表年份
/// </summary>
public int PostYear { get; set; }
/// <summary>
/// 文章备注
/// </summary>
public string Remark { get; set; }
}

AutoMapper实体转换

  从上面ArticleDTO中可以看到多了两个属性:Summary(文章摘要)和PostYear(文章发表年份),这就是我们在特定的业务场景中需要的,并不会影响到领域模型,如何实现领域模型和DTO之间的转换?我们可以使用AutoMapper可以很方便的对他们进行转换,一个强大的Object-Object Mapping工具。

  工具-库程序包管理器-程序包管理控制平台,输入“Install-Package AutoMapper”命令,就可以把AutoMapper添加到项目中,有关AutoMapper相关文档可以参考:https://github.com/AutoMapper/AutoMapper/wiki,这边简单演示下Article到ArticleDTO之间的转换。

         static void Main(string[] args)
{
Article article = new Article
{
Title = "漫谈实体、对象、DTO及AutoMapper的使用",
Content = "实体(Entity)、对象(Object)、DTO(Data Transfer Object)数据传输对象,老生常谈话题,简单的概念,换个角度你会发现更多的东西。个人拙见,勿喜请喷。",
Author = "xishuai",
PostTime = DateTime.Now,
Remark = "文章备注"
};
//配置AutoMapper
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Article, ArticleDTO>()//创建映射
.ForMember(dest => dest.ArticleID, opt => opt.MapFrom(src => src.Id))//指定映射规则
.ForMember(dest => dest.Summary, opt => opt.MapFrom(src => src.Content.Substring(, )))//指定映射规则
.ForMember(dest => dest.PostYear, opt => opt.MapFrom(src => src.PostTime.Year))//指定映射规则
.ForMember(dest => dest.Remark, opt => opt.Ignore());//指定映射规则 忽视没有的属性
}); //调用映射
ArticleDTO form = AutoMapper.Mapper.Map<Article, ArticleDTO>(article);
}

  转换效果:

后记

  其实这篇文章原本只是想简单写下DTO及AutoMapper的用法,但是不知怎的写着写着就写跑偏了,现在回过头看也不知道自己写了些什么东西,正如张三丰教张无忌太极剑法,问他记得了多少,最后什么都没记得。

  随着DDD(领域驱动设计)的学习和对道德经的感悟,就会发觉:此两者同出而异名,同谓之玄,玄之又玄,众妙之门。

  如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^

  参考资料:

【道德经】漫谈实体、对象、DTO及AutoMapper的使用的更多相关文章

  1. 应用程序框架实战三十四:数据传输对象(DTO)介绍及各类型实体比较

    本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行比较. 领域实体为何不能一统江湖? 当你阅读 ...

  2. 数据传输对象(DTO)介绍及各类型实体比较

    数据传输对象(DTO)介绍及各类型实体比较 本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行 ...

  3. ASP.NET Core搭建多层网站架构【8.2-使用AutoMapper映射实体对象】

    2020/01/29, ASP.NET Core 3.1, VS2019, AutoMapper.Extensions.Microsoft.DependencyInjection 7.0.0 摘要:基 ...

  4. ABP理论学习之数据传输对象(DTO)

    返回总目录 本篇目录 为何需要DTO 领域层抽象 数据隐藏 序列化和懒加载问题 DTO惯例和验证 DTO和实体的自动映射 使用特性和扩展方法进行映射 帮助接口 DTO用于应用层和 展现层间的数据传输. ...

  5. .NET的对象映射工具AutoMapper使用笔记

    AutoMapper是一个.NET的对象映射工具. 项目地址:https://github.com/AutoMapper/AutoMapper. 帮助文档:https://github.com/Aut ...

  6. 一行code实现ADO.NET查询结果映射至实体对象。

    AutoMapper是一个.NET的对象映射工具. 主要用途 领域对象与DTO之间的转换.数据库查询结果映射至实体对象. 这次我们说说 数据库查询结果映射至实体对象. 先贴一段代码: public S ...

  7. 对象映射工具AutoMapper介绍

    AutoMapper是用来解决对象之间映射转换的类库.对于我们开发人员来说,写对象之间互相转换的代码是一件极其浪费生命的事情,AutoMapper能够帮助我们节省不少时间. 一. AutoMapper ...

  8. .NET Core Dto映射(AutoMapper)

    .Net Core Dto映射(AutoMapper) 我们假设一个场景, 采用EF Core+Web Api, 这时候可能会出现EF Core中的Entity Model和在项目中使用的Model之 ...

  9. 利用HttpWebRequest实现实体对象的上传

    一 简介 HttpWebRequest和HttpWebResponse类是用于发送和接收HTTP数据的最好选择.它们支持一系列有用的属性.这两个类位 于System.Net命名空间,默认情况下这个类对 ...

随机推荐

  1. 戏说HTML5

    如果有非技术人员问你,HTML5是什么,你会怎么回答? 新的HTML规范... 给浏览器提供了牛逼能力,干以前不能干的事...(确切地说应该是给浏览器规定了许多新的接口标准,要求浏览器实现牛逼的功能. ...

  2. MySQL设置字段的默认值为当前系统时间

    问题产生: 当我们在对某个字段进行设置时间默认值,该默认值必须是的当前记录的插入时间,那么就将当前系统时间作为该记录创建的时间. 应用场景: 1.在数据表中,要记录每条数据是什么时候创建的,应该由数据 ...

  3. 在jekyll模板博客中添加网易云模块

    最近使用GitHub Pages + Jekyll 搭建了个人博客,作为一名重度音乐患者,博客里面可以不配图,但是不能不配音乐啊. 遂在博客里面引入了网易云模块,这里要感谢网易云的分享机制,对开发者非 ...

  4. AJAX 大全

    本章内容: 简介 伪 AJAX 原生 AJAX XmlHttpRequest 的属性.方法.跨浏览器支持 jQuery AJAX 常用方法 跨域 AJAX JsonP CORS 简单请求.复制请求.请 ...

  5. PHP设计模式(四)单例模式(Singleton For PHP)

    今天讲单例设计模式,这种设计模式和工厂模式一样,用的非常非常多,同时单例模式比较容易的一种设计模式. 一.什么是单例设计模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对 ...

  6. “此网页上的某个 Web 部件或 Web 表单控件无法显示或导入。找不到该类型,或该类型未注册为安全类型。”

    自从vs装了Resharper,看见提示总是手贱的想去改掉它.于是乎手一抖,把一个 可视web部件的命名空间给改了. 喏,从LibrarySharePoint.WebPart.LibraryAddEd ...

  7. Git使用详细教程(二)

    分支 其实在项目clone下来后就有一个分支,叫做master分支.新建分支的步骤:右键项目→Git→Repository...→Branches... master分支应该是最稳定的,开发的时候,建 ...

  8. SpringMVC_简单小结

    SpringMVC是一个简单的.优秀的框架.应了那句话简单就是美,而且他强大不失灵活,性能也很优秀. 机制:spring mvc的入口是servlet,而struts2是filter(这里要指出,fi ...

  9. hive

    Hive Documentation https://cwiki.apache.org/confluence/display/Hive/Home 2016-12-22  14:52:41 ANTLR  ...

  10. Linux CentOS7通过yum命令安装Mono(尝先安装模式)

    前言 经过尝试网上各种安装mono的技术贴,这个安装过程经历了大约2周,尝试了各个版本,几目前博客所描述的所有安装方式.以下内容的安装方式可以为你尝试不同版本的mono.并非正式环境安装标准方式安装. ...