【DDD】持久化领域对象的方法实践
概述
在实践领域驱动设计(DDD)的过程中,我们会根据项目的所在领域以及需求情况捕获出一定数量的领域对象。设计得足够好的领域对象便于我们更加透彻的理解业务,方便系统后期的扩展和维护,不至于随着需求的扩展和代码量的累积,系统逐渐演变为大泥球(Big Ball of Mud)。
虽然领域驱动设计的思想很诱人,但我们依然会面临各种隐藏的困难,就比如今天我们要讲的主题“持久化”:即使前期我们设计了足够完整的领域对象,但是依然需要持久化它们到数据库中,而普通的关系型数据库可能很难维持领域对象的原有结构,所以我们必须要使用一些特有的手段来处理它。
开篇
本篇文章属于《如何运用领域驱动设计》系列的一个补充,如果您阅读过该系列的其它文章,您就会发现关于“持久化”的这个问题已经不止在一篇博文中提及到了。
那么,到底是什么原因让我们面临这个问题呢? 是的!值对象! 如果您认真的了解过值对象的话(如果还不了解值对象,您可以参考 如何运用领域驱动设计 - 值对象),您会发现值对象是由许多基元类型构成的(比如string,int,double等),所以我们可以理解它为对细粒度基元类型的包裹,构成我们所在领域中的一个基础类型,比如说下面这个例子:
public sealed class City : ValueObject
{
public string Name { get; }
public int Population { get; }
public City(string name, int population)
{
Name = name;
Population = population;
}
}
我们假设现在有一个叫做City的值对象,它是由名称(Name)和人口数量(Population)构成。通常我们这样建立值对象的原因很简单,在该领域中我们一联系到“人口”数量就会和“城市”连同在一起(你不会说我想知道人口数量,而你会说我想知道纽约的人口数量),所以“城市”这一概念成为我们该领域中的小颗粒对象,而该对象在代码实现中是由多个小基元类型构成的,比如该例子就是由一个string和一个int。
这样建模的好处之一就是我们考虑的问题是一个整体,将零碎的点构建为一个整体对象,如果该对象的行为需要发生改变,只需要修改该对象本身就可以了,而不是代码散落在各处需要到处查找(这也是滚成大泥球的原因之一)。
如果您喜欢捕猎有关DDD的知识,您可能不止一次会看到这样一条建议规则:
In the world of DDD, there’s a well-known guideline that you should prefer Value Objects over Entities where possible. If you see that a concept in your domain model doesn’t have its own identity, choose to treat that concept as a Value Object.
该建议的内容就是提倡DDD实践者多使用值对象。当然也不是说无论什么东西都建立成值对象,只是要我们多去发现领域中的值对象。
但是这往往给持久化带来了难度,先来想一下传统的编码持久化方式:一个对象(或者POCO)里面包含了各个基元类型的属性,当需要持久化时,每个属性都对应数据库的一个字段,而该对象就成为了一个表。 但是这在领域驱动设计中就不好使用了,值对象成了我们考虑问题的小颗粒,而它在代码中成了一个类,如果直接持久化它是什么样子呢?表,使用它的实体或者聚合根也是一个表,两个表通过主外键关系链接。
那么这样持久化方式好不好呢? 答案是不确定的,可能了解了下文的这些方案后,您会有自己的见解。
本篇文章的持久化方案都是基于关系型数据库,如果您是非关系型数据库(比如mongodb),那么您应该不会面临这样的问题。
字段 Or 表
将值对象持久化成字段好呢?还是将值对象持久化为表好呢? 这个问题其实也有很多广泛的讨论,就好比.NET好还是Java好(好吧,我php天下**),目前其实也没有个明确的结果:
- 觉得持久化为表字段的原因是 如果持久化为表,必须给表添加一个ID供引用的实体或者聚合关联,这就不满足值对象不应该有ID的准则了。
- 觉得持久化为表的原因是 数据表模型并不代表代码层面的模型,代码里面的值对象其实并没有ID的说法,所以它是符合值对象的,而持久化为字段的话,同一个值对象数据会被复制为多份导致数据冗余。
当然哈,各有各的道理,我们也不用特别偏向于使用哪个结论。应该站在客观的角度,实际的项目需要哪种手段就根据切实的情况来选择。
来说一下持久化为字段的情况
该手段其实在近期来说比较流行,特别是在EFCore2.0之后,为什么呢?因为EF Core2.0提供了一个叫做 从属实体类型 的概念,其实这个技术手段在EF中很早就有了,在EF中有一个叫做Complex的东西,只是在EF Core 1.x时代没有引入而已。
在EFCore引入了Owned之后,微软那个最著名的微服务教程 eShopOnContainers 也顺势推出了用于该特性来持久化值对象的方案:

所以这也是为什么大家都在使用Owned持久化值对象的原因。(当然,大家项目中只有Address被建立为值对象的习惯不知道是不是从这儿养成的
【DDD】持久化领域对象的方法实践的更多相关文章
- 我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践
写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听你,清风吹送,田野短笛:第一次看你,半弯 ...
- DDD领域驱动设计和实践(转载)
-->目录导航 一. DDD领域驱动设计介绍 1. 什么是领域驱动设计(DDD) 2. 领域驱动设计的特点 3. 如果不使用DDD? 4. 领域驱动设计的分层架构和构成要素 5. 事务脚本和领域 ...
- DDD(领域驱动设计)理论结合实践
DDD(领域驱动设计)理论结合实践 写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听 ...
- 【DDD】领域驱动设计实践 —— 框架实现
本文主要了在社区服务系统(ECO)中基于SpringMVC+mybatis框架对DDD的落地实现.本文为系列文章中的其中一篇,其他内容可参考:通过业务系统的重构实践DDD. 框架实现图 该框架实现基本 ...
- 【DDD】领域驱动设计实践 —— Application层实现
本文是DDD框架实现讲解的第二篇,主要介绍了DDD的Application层的实现,详细讲解了service.assemble的职责和实现.文末附有github地址.相比于<领域驱动设计> ...
- 【DDD】领域驱动设计实践 —— Domain层实现
本文是DDD框架实现讲解的第三篇,主要介绍了DDD的Domain层的实现,详细讲解了entity.value object.domain event.domain service的职责,以及如何识别出 ...
- [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)
一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...
- 【DDD】领域驱动设计实践 —— UI层实现
前面几篇blog主要介绍了DDD落地架构及业务建模战术,后续几篇blog会在此基础上,讲解具体的架构实现,通过完整代码demo的形式,更好地将DDD的落地方案呈现出来.本文是架构实现讲解的第一篇,主要 ...
- 一缕阳光:DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?
写在前面 阅读目录: 问题根源是什么? <领域驱动设计-软件核心复杂性应对之道>分层概念 Repository(仓储)职责所在? Domain Model(领域模型)重新设计 Domain ...
随机推荐
- sublime text 3创建新文件插件-AdvanceNewFile
这里要记录sublime text 3 在创建新文件时安装的插件–AdvanceNewFile ST本来自带的创建新文件的快捷键是ctrl+n.但是用户需要保存时才可修改名称以及文件路径.但是安装完A ...
- 解析P2P金融的业务安全
看了很多乙方同学们写的业务安全,总结下来,其出发点主要是在技术层面风险问题.另外捎带一些业务风险.今天我要谈的是甲方眼里的业务安全问题,甲方和乙方在业务安全的视野上会有一些区别和一些重合.在同一个问题 ...
- laravel 授权使用gate门类
第一:先注册 第二:使用方式三种 路由中:Route::group(['middleware'=>'can:system'],function() {}) 模板中:@can("syst ...
- 弹性FLEX布局
页面布局一直都是web应用样式设计的重点 我们传统的布局方式都是基于盒模型的 利用display.position.float来布局有一定局限性 比如说实现自适应垂直居中 随着响应式布局的流行,CSS ...
- javascript 宽度和高度
宽度和高度 对于编写css代码时,宽度和高度就是width和height 但是在JavaScript中,还有其他的宽度和高度,比如offsetWidth,offsetHeight,clientX,cl ...
- 列出display的值,说明他们的作用。position的值, relative和 absolute定位原点是?
display的值: block 像块类型元素一样显示. none 像行内元素类型一样显示. inline-block 像行内元素一样显示, 但其内容像块类型元素一样显示. list-item 像块类 ...
- Python--day67--Jsonresponse响应介绍和路由系统的分组命名匹配方式(简单介绍)
1,Jsonresponse响应介绍: ,2,路由系统的分组命名匹配方式:(简单介绍)
- Codeforces Round #167 (Div. 1 + Div. 2)
C. Dima and Staircase 线段树维护区间最大值. D. Dima and Two Sequences 由于模数不一定为质数,所以通过拆分质因数来做阶乘取模. E. Dima and ...
- [转载] linux下tar命令解压到指定的目录
参考 http://blog.sina.com.cn/s/blog_62449fcf0100nfar.html linux下tar命令解压到指定的目录 : #tar zxvf /bbs.tar.z ...
- 第25章 Pytorch 如何高效使用GPU
第25章 Pytorch 如何高效使用GPU 深度学习涉及很多向量或多矩阵运算,如矩阵相乘.矩阵相加.矩阵-向量乘法等.深层模型的算法,如BP,Auto-Encoder,CNN等,都可以写成矩阵运算的 ...