【道德经】漫谈实体、对象、DTO及AutoMapper的使用
写在前面
实体(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(领域驱动设计)的学习和对道德经的感悟,就会发觉:此两者同出而异名,同谓之玄,玄之又玄,众妙之门。
如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^
参考资料:
- http://www.cnblogs.com/dudu/archive/2011/12/16/2284828.html
- http://www.cnblogs.com/ego/archive/2009/05/13/1456363.html
- http://www.cnblogs.com/jiguixin/archive/2011/09/19/2181521.html
- http://blog.csdn.net/yujunwu2525/article/details/7850486
【道德经】漫谈实体、对象、DTO及AutoMapper的使用的更多相关文章
- 应用程序框架实战三十四:数据传输对象(DTO)介绍及各类型实体比较
本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行比较. 领域实体为何不能一统江湖? 当你阅读 ...
- 数据传输对象(DTO)介绍及各类型实体比较
数据传输对象(DTO)介绍及各类型实体比较 本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行 ...
- ASP.NET Core搭建多层网站架构【8.2-使用AutoMapper映射实体对象】
2020/01/29, ASP.NET Core 3.1, VS2019, AutoMapper.Extensions.Microsoft.DependencyInjection 7.0.0 摘要:基 ...
- ABP理论学习之数据传输对象(DTO)
返回总目录 本篇目录 为何需要DTO 领域层抽象 数据隐藏 序列化和懒加载问题 DTO惯例和验证 DTO和实体的自动映射 使用特性和扩展方法进行映射 帮助接口 DTO用于应用层和 展现层间的数据传输. ...
- .NET的对象映射工具AutoMapper使用笔记
AutoMapper是一个.NET的对象映射工具. 项目地址:https://github.com/AutoMapper/AutoMapper. 帮助文档:https://github.com/Aut ...
- 一行code实现ADO.NET查询结果映射至实体对象。
AutoMapper是一个.NET的对象映射工具. 主要用途 领域对象与DTO之间的转换.数据库查询结果映射至实体对象. 这次我们说说 数据库查询结果映射至实体对象. 先贴一段代码: public S ...
- 对象映射工具AutoMapper介绍
AutoMapper是用来解决对象之间映射转换的类库.对于我们开发人员来说,写对象之间互相转换的代码是一件极其浪费生命的事情,AutoMapper能够帮助我们节省不少时间. 一. AutoMapper ...
- .NET Core Dto映射(AutoMapper)
.Net Core Dto映射(AutoMapper) 我们假设一个场景, 采用EF Core+Web Api, 这时候可能会出现EF Core中的Entity Model和在项目中使用的Model之 ...
- 利用HttpWebRequest实现实体对象的上传
一 简介 HttpWebRequest和HttpWebResponse类是用于发送和接收HTTP数据的最好选择.它们支持一系列有用的属性.这两个类位 于System.Net命名空间,默认情况下这个类对 ...
随机推荐
- 微信小程序开发心得
微信小程序也已出来有一段时间了,最近写了几款微信小程序项目,今天来说说感受. 首先开发一款微信小程序,最主要的就是针对于公司来运营的,因为,在申请appid(微信小程序ID号)时候,需要填写相关的公司 ...
- MVVM模式和在WPF中的实现(一)MVVM模式简介
MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...
- 获取Canvas当前坐标系矩阵
前言 在我的另一篇博文 Canvas坐标系转换 中,我们知道了所有的平移缩放旋转操作都会影响到画布坐标系.那在我们对画布进行了一系列操作之后,怎么再知道当前矩阵数据状态呢. 具体代码 首先请看下面的一 ...
- DOM、BOM 操作超级集合
本章内容: 定义 节点类型 节点关系 选择器 样式操作方法style 表格操作方法 表单操作方法 元素节点ELEMENT 属性节点attributes 文本节点TEXT 文档节点 Document 位 ...
- MFC中成员变量的声明顺序与析构顺序
第一次用博客,第一篇随笔,就写今天遇到的一个问题吧. 在VS2008的MFC对话框程序,窗口成员变量的声明顺序与其析构顺序相反,即,先声明的变量后析构,后声明的变量先析构.未在其他模式下测试. cla ...
- iOS开源项目周报1215
由OpenDigg 出品的iOS开源项目周报第一期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开发方面的开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等. PY ...
- 敏捷转型历程 - Sprint4 回顾会
我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...
- POJ2774 Long Long Message [后缀数组]
Long Long Message Time Limit: 4000MS Memory Limit: 131072K Total Submissions: 29277 Accepted: 11 ...
- 机器指令翻译成 JavaScript —— No.6 深度优化
第一篇 中我们曾提到,JavaScript 最终还得经过浏览器来解析.因此可以把一些优化工作,交给脚本引擎来完成. 现代浏览器的优化能力确实很强,但是,运行时的优化终归是有限的.如果能在事先实现,则可 ...
- 一步步学习javascript基础篇(9):ajax请求的回退
需求1: ajax异步请求 url标识请求参数(也就是说复制url在新页面打开也会是ajax后的效果) ajax异步请求没问题,问题一般出在刷新url后请求的数据没了,这就是因为url没有记录参数.如 ...