概述

在实践领域驱动设计(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】持久化领域对象的方法实践的更多相关文章

  1. 我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践

    写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听你,清风吹送,田野短笛:第一次看你,半弯 ...

  2. DDD领域驱动设计和实践(转载)

    -->目录导航 一. DDD领域驱动设计介绍 1. 什么是领域驱动设计(DDD) 2. 领域驱动设计的特点 3. 如果不使用DDD? 4. 领域驱动设计的分层架构和构成要素 5. 事务脚本和领域 ...

  3. DDD(领域驱动设计)理论结合实践

    DDD(领域驱动设计)理论结合实践   写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听 ...

  4. 【DDD】领域驱动设计实践 —— 框架实现

    本文主要了在社区服务系统(ECO)中基于SpringMVC+mybatis框架对DDD的落地实现.本文为系列文章中的其中一篇,其他内容可参考:通过业务系统的重构实践DDD. 框架实现图 该框架实现基本 ...

  5. 【DDD】领域驱动设计实践 —— Application层实现

    本文是DDD框架实现讲解的第二篇,主要介绍了DDD的Application层的实现,详细讲解了service.assemble的职责和实现.文末附有github地址.相比于<领域驱动设计> ...

  6. 【DDD】领域驱动设计实践 —— Domain层实现

    本文是DDD框架实现讲解的第三篇,主要介绍了DDD的Domain层的实现,详细讲解了entity.value object.domain event.domain service的职责,以及如何识别出 ...

  7. [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)

    一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...

  8. 【DDD】领域驱动设计实践 —— UI层实现

    前面几篇blog主要介绍了DDD落地架构及业务建模战术,后续几篇blog会在此基础上,讲解具体的架构实现,通过完整代码demo的形式,更好地将DDD的落地方案呈现出来.本文是架构实现讲解的第一篇,主要 ...

  9. 一缕阳光:DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?

    写在前面 阅读目录: 问题根源是什么? <领域驱动设计-软件核心复杂性应对之道>分层概念 Repository(仓储)职责所在? Domain Model(领域模型)重新设计 Domain ...

随机推荐

  1. jq杂项方法/工具方法----each() grep() map()

    each() 用于循环数组 对象(单纯遍历) 返回 false 可提前停止循环.接受的参数是数组名和要执行的函数,函数参数为数组索引和当前元素. var arr = [30, 40, 50,1 ,8] ...

  2. Python--day70--ORM一对一表结构

    ORM一对一表结构:

  3. Python--day69--ORM外键的正向查询和反向查询

    什么是正向查询,什么是方向查询?

  4. hdu 4146 Flip Game

    Flip Game Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total ...

  5. 谷歌浏览器中kindeditor编译器字体不能为微软雅黑的问题?

    https://segmentfault.com/q/1010000006204144 比如说用谷歌浏览器打开后台编译文章,在文章先选择字体为微软雅黑,再编辑其他,哪个字体就变成了&quot: ...

  6. java接口的使用

    格式:public class SubImpl  extends Super  implements IA,IB 接口可以多继承,但是只能继承接口,不能继承类. 实现接口(支持多实现) [修饰符] c ...

  7. 【React】react项目引入echarts插件 K线图

    参考npm文档:https://www.npmjs.com/package/echarts-for-react 由于npm上已经有针对react项目出的echarts插件,所以在这里直接安装 第一步: ...

  8. java Class中得到构造方法Constructor、方法Method、字段Field

    常用方法: Constructor类用于描述类中的构造方法: Constructor<T> getConstructor(Class<?>... parameterTypes) ...

  9. H3C DHCP中继配置示例

  10. java Io流的应用

                                                                         标准输入输出流 1.1标准输入流 源数据源是标准输入设备(键盘 ...