前言

哈喽大家好,DDD领域驱动设计系列又开始了,前天周二的那篇入门文章中,也收到了一定的效果(写小说的除外),同时我也是倍感鸭梨,怎么说呢,DDD领域驱动设计已经有十年历史了,甚至更久,但是包括我在内的一批技术人员还是对其不是很明白,这几天我也是日思夜想,怎样才能说的明白,怎样才能把这个高高在上的思想落在实践上,可惜的是国内栗子比较少,国外文章比较少,只能硬啃了,所以更需要大家一起来讨论,这里要说一下,是一起讨论推动,而不是内心去拒绝,而一直和多层架构做对比,这样不仅不利于学习,也无法带动我的积极性,所以,这里恳请大家,多多评论,多多交流,比较我一个人很难扛得动这个DDD的大旗。

好啦,言归正传,上次咱们说到了《[ DDD ] 之二 ║ DDD入门 & 项目结构粗搭建》,其中主要说明了为什么使用DDD,以及如何简单的搭建一个基于领域的粗略层,颗粒度还是项目级别,还没有继续往下深究,今天呢,咱们就往下慢慢走,说一说整个项目下,是如何实现领域设计的。

这里先给大家提一个问题,如果一个新的项目,比如一个小的问答系统交给你的手里,PM 刚刚和你简单的讨论了下需求,下一步你打算做些什么?

1、根据需求,立刻准备设计数据库,建表,脑中模拟场景;

2、根据需求,立刻建立实体类(也就是model层),然后CodeFirst 生成数据库;

3、找寻该领域专家(做过或者懂得类似产品的人),设计该问答领域下,有哪些子领域,制作限界上下文;

4、啥都没有,直接网上找开源项目,下载下来看看;

老张说:这里没有正确与否的比较,只是一个习惯和优劣的分析,不用太在意,如果你比较好奇,那就往下看吧。

零、今天要完成绿色的部分

一、领域 —— 就是一个独立项目

1、领域的概念

这个概念相信很多人已经很明白了,而且也听到了无数遍,这里就再简单的说两句:

领域(Domain)其实就是一个组织所要做的整个事情,已经这个事情下所包含的一切内容。这是一个范围概念,而且是面向业务的(注意这里不是面向技术的,更不是面向数据库的持久化的),每个组织都有自己的人员、自己的工作业务范围和做事方式,当你为该组织开发软件的时候,你面对的就是这个组织的领域。

就比如之前我在一家旅游公司进行开发工作,那我所进行的开发工作就是一个旅游行业,我必须要很清晰旅游行业的其中的领域知识,而且必须能和领域专家通过通用性语言进行沟通,这样能保证我开发出来的是他们想要的,而不是我单纯的从技术上实现,在领域设计上一塌糊涂。当然我们每天也都在做这样的事情,也许你感觉很正常,那我再举个例子:

我在开发其中一个目的地(旅游景点)项目的时候,这是一个领域,后来在电商系统项目中,又是一个领域,但是在电商领域中,涉及到了景点领域的一些数据,那我如果不和领域专家沟通,有时候为了贪图技术上的方便,甚至把两个领域合并成一个,虽然都不大,合并以后大小也还可以,但是这样却完全打破了领域的这个概念,这个就是完全面向技术开发的,因为领域专家看不懂我这么写到底属于什么。

当然上边的栗子有点儿牵强,咱们再说下以后我想做的一个基于DDD的问答项目,咱们先画一个框。

就如图所示,咱们首先定义一个边界,至于里边有什么东西,咱们接着往下看,这个很简单。

2、如何定义一个领域

这个是更简单的一个问题,在领域设计中,有两个方法:战略设计和战术设计,其实我个人感觉可以定义两步走,这两个是有先后之分的,

战略设计中定义了,一个领域就是一个问题空间,我们在业务中所遇到的所有的问题与挑战;

在战术设计中,一个领域就算一个解决问题空间,用来解决在问题空间的所有问题;

所以,其实一个领域就是一个我们建立的一个解决方案,一个项目,在我们的问答项目中,整个解决方案就是一个问答领域。

二、子领域 —— 具体的项目实现

1、子域 / 核心子领域 / 通用子领域

什么是子域(SubDomain)呢?这个很好理解,就是在整个领域中,我们如何对其进行拆分,然后满足我们的业务逻辑。一个子域可能是一个 dll ,一个命名空间的形式存在。

我们定义好领域,并且划分好限界后,就开始考虑如何进行实现,这里大家想一想如何设计与划分,这里就说说我自己的之前的想法:

在我们的问答领域设计中,我们的思路一定是有客户来 =》验证是否有发问题的权限 =》 然后发布一个问题 =》

这仅仅是一个发布问题的流程,也仅仅是一个顾客认证的过程,很简单,我们一般会怎么分子领域呢,可能会这么分,这个就是 消息发布子领域,里边有我们的发布模型,用户模型,讨论模型,日历模型等等,大概就是这个样子

因为我们会这么想:“用户和权限这两个模型,和我们的消息子领域有何紧密的关系,你看,发布+回复+讨论+日历(指自己新建一个日历功能,具体待定),这些模型肯定都需要用户登陆认证吧,甚至有些是需要授权的,分在一个子领域有什么不对么?”,这样的代码逻辑应该是这样的

如果是你看到这里,首先明白了什么是子领域了吧,也知道如何划分了,但是你感觉这个划分对么? 如果你感觉很正常,那就请往下看吧。

2、核心子领域 / 通用子领域 / 支撑子领域

我们再来分析一下,我们的问答领域中的有哪些内容,首先:肯定有消息发布子领域,这个也是上边说到的,这个毋庸置疑,一个问答系统,消息发布是肯定的(这里说明下:发布问题,回答问题,讨论问题等都属于一个消息的发布,这个应该理解),而且这个子领域是缺少它不可的,这个就是我们的核心子领域

再来看看,还有一些其他的,比如日志记录,数据操作痕迹记录(哪个管理员修改了哪些数据),这些子领域贯穿着我们整个领域系统,被其他领域共用,我们称之为 通用子领域

当然,我们还有一些站内的即时消息,wiki百科,通知提醒,活动跟踪,等等,这些都不是我们的核心子域,因为没有这些,我们依然可以进行问答,但是这些确是支撑着我们核心子域的相关功能,我们就把这些命名为 支撑子领域,这个时候你会问,这些支持子领域要不要再拆开,我个人表示没有很大的必要。

最后我们再来看看我们上边的用户认证授权问题,在上边我们把他们柔和到了消息核心子域里,但是这里要说明,这两者是没有关系的:

为什么没有关系呢?诚然,我们的软件是必须有用户参与的,但是我们应该将不同的用户种类区别对待,因为在不同的上下文(下边会说到)中,他们的作用和任务是不一样的,在消息核心子域中,我们关注的是角色,不管他是谁或者有什么权限,如果我们有一天把权限模型修改了,那我们的问答模型也一定要修改,你想想是不是,因为两者业务逻辑已经耦合了!

这个时候我们应该明白,发布信息和“谁可以发,在什么条件下发”其实没有太大的关系,我的问答,只关心的是“有一个顾客发布了一个问题”这样就可以了,我们关心的是发布消息这个过程,而不能把用户权限涉及进来,这个时候我们应该把用户权限单拿出来一个子领域,就叫安全子领域。

3、隔离内核

其实上边说的可能有点儿朦胧,但是我们应该都已经用到了,如果你看了我的上一个系列教程,你应该知道有一个JWT权限验证那一章节,很多人就是不很理解,是如何进行授权验证的,其实采用的就是隔离内容,以前我们写逻辑,就算直接在控制器里,判断当前用户权限,但是现在我们是通过一个中间件,判断 Token 所包含的用户Role 是否有这个权限,再进行下一步,只不过在DDD中,把这一块单拿出来形成了一个安全子领域了,这个时候你应该明白了吧。

        /// <summary>
/// 删除一个顾客信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpPost, ActionName("Delete")]
[Authorize(Policy = "CanRemoveCustomerData")]
[Route("customer-management/remove-customer/{id:guid}")]
[ValidateAntiForgeryToken]
public IActionResult DeleteConfirmed(Guid id)
{
_customerAppService.Remove(id); if (!IsValidOperation()) return View(_customerAppService.GetById(id)); ViewBag.Sucesso = "Customer Removed!";
return RedirectToAction("Index");
}

这个时候,可能还不是很明白,为什么好好的程序要拆分,这么做的目的又是为了什么,直接在需要用到的权限的地方写业务逻辑不就行了么,这个往下看,咱们说说限界上下文。

三、限界上下文 —— 领域模型的边界

1、限界上下文是显示的,有语义的

限界上下文(Bounded Context)定义了每个模型的应用范围,在每个Bounded Context中确保领域模型的一致性。不同的限界上下文中,领域模型可以不用保证一致性。通常我们根据团队的组织、软件系统的每个部分的用法及物理表现(如组件划分,数据库模式)来设置模型的边界。

概念还是有点儿朦胧,那就举例来说:

在电商系统中,销售子域是核心域,商品子域和物流子域为支撑子域。在这三个子域中,都要和商品打交道。如果把商品抽象为Product对象的话,按我们一般的常规思路(抛开子域的划分)来说,不管是商品销售还是发货,我们都可以共用同一个Product对象。
但在DDD中,在商品子域和销售子域中,可以共享这个Product对象,但在物流子域,就有点大材小用。为什么呢?因为毕竟物流子域关注的是商品的发货处理和物流跟踪。针对发货流程而言,我只关心商品的数量、大小、重量等规格,而不必了解商品的价格等其他信息。所以说物流子域应该关注的是货物的发货处理而不是商品。
那为什么我们之前的开发思路会共用同一个Product对象呢?
答案很简单,没有进行领域的划分。把整个项目一概而论,统一建模导致的结果。
在DDD的思想下,当划分子域之后,每个子域都对应有各自的上下文。在销售子域和商品子域所在的上下文语境中,商品就是商品,无二义性。在物流子域的上下文语境中,我们也可以说商品的发货处理,但这时的商品就特指货物了。确定了真实面目之后,我想我们也会不由自主的抽象一个新的Cargo对象来处理物流相关的业务。这也是DDD带来的好处,让我们更清晰的建模。

2、定义限界上下文

在我们上边的子域定义中,我们出现了三个子域,我这里同时也定义了三个限界上下文(这里说下,两者不是一对一的关系),总体来说,我们不应该按技术架构或者开发任务来创建限界上下文,应该按照语义的边界来考虑。

我们的实践是,考虑产品所讲的通用语言,从中提取一些术语称之为概念对象,寻找对象之间的联系;或者从需求里提取一些动词,观察动词和对象之间的关系;我们将紧耦合的各自圈在一起,观察他们内在的联系,从而形成对应的界限上下文。形成之后,我们可以尝试用语言来描述下界限上下文的职责,看它是否清晰、准确、简洁和完整。简言之,限界上下文应该从需求出发,按领域划分。

3、上下文都包含哪些内容

一个限界上下文不是只有领域模型,当然这个是必不可少的,它总体来说是一个系统,一个应用程序,或者一个业务服务,它里边会有实体,值对象,领域事件(一个个方法事件组成,比如用户注册,修改密码,验证信息等等都是该上下文中的领域事件),在我们的身份和访问上下文中,是这样定义的

感觉写到这里还是没有写的很透彻,因为我们还没有涉及到代码,可能通过代码的设计会比较好。

四、结语

本文主要是通过DDD领域设计的思想,来说明如何对一个项目进行细分的过程,这个再想想文章开头提出的问题,是不是稍微有些感触,只不过在没有代码的讲解下,一起总是很空洞,下次咱们直接通过基础设施层中的上下文定义,来进一步了解领域设计的思想吧。

从壹开始微服务 [ DDD ] 之三 ║ 简单说说:领域、子域、限界上下文的更多相关文章

  1. 从壹开始微服务 [ DDD ] 之八 ║剪不断理还乱的 值对象和Dto

    缘起 哈喽大家周四好,时间是过的真快,这几天一直忙着在公司的项目,然后带带新人,眼看这周要过去了,还是要抽出时间学习学习,这些天看到群里的小伙伴也都在忙着新学习,还是很开心的,至少当时的初衷已经达到了 ...

  2. 从壹开始微服务 [ DDD ] 之六 ║聚合 与 聚合根 (下)

    前言 哈喽大家周二好,上次咱们说到了实体与值对象的简单知识,相信大家也是稍微有些了解,其实实体咱们平时用的很多了,基本可以和数据库表进行联系,只不过值对象可能不是很熟悉,值对象简单来说就是在DDD领域 ...

  3. 从壹开始微服务 [ DDD ] 之七 ║项目第一次实现 & CQRS初探

    前言 哈喽大家周五好,我们又见面了,感谢大家在这个周五读我的文章,经过了三周的时间,当然每周两篇的速度的情况下,咱们简单说了下DDD领域驱动设计的第一部分,主要包括了,<项目入门DDD架构浅析& ...

  4. 从壹开始微服务 [ DDD ] 之终篇 ║当事件溯源 遇上 粉丝活动

    回首 哈喽~大家好,时间过的真快,关于DDD领域驱动设计的讲解基本就差不多了,本来想着周四再开一篇,感觉没有太多的内容了,剩下的一个就是验证的问题,就和之前的JWT很类似,就不打开一个章节了,而且这个 ...

  5. 从壹开始微服务 [ DDD ] 之十二 ║ 核心篇【下】:事件驱动EDA 详解

    缘起 哈喽大家好,又是周二了,时间很快,我的第二个系列DDD领域驱动设计讲解已经接近尾声了,除了今天的时间驱动EDA(也有可能是两篇),然后就是下一篇的事件回溯,就剩下最后的权限验证了,然后就完结了, ...

  6. 从壹开始微服务 [ DDD ] 之十一 ║ 基于源码分析,命令分发的过程(二)

    缘起 哈喽小伙伴周三好,老张又来啦,DDD领域驱动设计的第二个D也快说完了,下一个系列我也在考虑之中,是 Id4 还是 Dockers 还没有想好,甚至昨天我还想,下一步是不是可以写一个简单的Angu ...

  7. 从壹开始微服务 [ DDD ] 之九 ║从军事故事中,明白领域命令验证(上)

    烽烟 哈喽大家周二好呀,咱们又见面了,上周末掐指一算,距离 圣诞节 只有 5 周的时间了(如果你还不知道为啥我要提圣诞节这个时间点,可以看看我的第二系列开篇<之一 ║ D3模式设计初探 与 我的 ...

  8. 从壹开始微服务 [ DDD ] 之一 ║ D3模式设计初探 与 我的计划书

    缘起 哈喽大家周四好!又是开心的一天,时间过的真快,我们的 <从壹开始 .net core 2.1 + vue 2.5>前后端分离系列共 34 篇已经完结了,当然以后肯定还会有更新和修改, ...

  9. 从壹开始微服务 [ DDD ] 之十 ║领域驱动【实战篇·中】:命令总线Bus分发(一)

    烽火 哈喽大家好,老张又见面了,这两天被各个平台的“鸡汤贴”差点乱了心神,博客园如此,简书亦如此,还好群里小伙伴及时提醒,路还很长,这些小事儿就随风而去吧,这周本不打算更了,但是被群里小伙伴“催稿”了 ...

随机推荐

  1. 6.app架构基础

    app架构,一个听起来高大尚的名字,很多小伙伴听到这个词语感觉很迷茫,不知道架构具体说的是啥?在q群里,"app后端应该怎么架构"这个问题被问了无数次.通过阅读本文,根据本人提出的 ...

  2. 在Windows Server 2008 R2下搭建jsp环境(三)-Tomcat的下载安装

    1.百度搜索"Tomcat官网",点击有标志的官网进入,准备下载官方正版Tomcat. 2.进入Tomcat官网之后,在左边我们看到,Tomcat的有6,7,8这三个最流行的版本, ...

  3. 【BZOJ 4016】 [FJOI2014]最短路径树问题

    题目链接: TP 题解:  我就是个智障.明明是道大水题,硬是拖了6h. 关于这道题我唯一想说的就是,记得更新拆分后的子树大小!!!我就是ZZ恒(QwQ. 代码: #define Troy 10/26 ...

  4. bzoj5252 [2018多省省队联测]林克卡特树

    斜率优化树形dp?? 我们先将问题转化成在树上选K+1条互不相交路径,使其权值和最大. 然后我们考虑60分的dp,直接维护每个点子树内选了几条路径,然后该点和0/1/2条路径相连 然后我们会发现最后的 ...

  5. 防XXS和SQL注入

    对网站发动XSS攻击的方式有很多种,仅仅使用php的一些内置过滤函数是对付不了的,即使你将filter_var,mysql_real_escape_string,htmlentities,htmlsp ...

  6. 深入css布局篇(3)完结 — margin问题与格式化上下文

    深入css布局(3) - margin问题与格式化上下文      在css知识体系中,除了css选择器,样式属性等基础知识外,css布局相关的知识才是css比较核心和重要的点.今天我们来深入学习一下 ...

  7. maven创建web报错Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:maven-compiler-plugin:maven-compiler-plugin:3.5.1:runtime Cause: error in opening zip file

    Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:maven-compiler-plugin:m ...

  8. [asp.net mvc 奇淫巧技] 06 - 也许你的项目同一个用户的请求都是同步的

    一.感慨 很久前看到一篇博客中有句话大致的意思是:“asp.net 程序性能低下的主要原因是开发人员技术参差不齐”,当时看到这句话不以为然,然而时间过的越久接触的.net 开发人员越多就越认同这句话: ...

  9. python中报错"json.decoder.JSONDecodeError: Expecting value:"的解决

    在学习python语言中用json库解析网络数据时,我遇到了两个编译错误:json.decoder.JSONDecodeError: Expecting property name enclosed ...

  10. 从壹开始 [ Id4 ] 之一║ 授权服务器 IdentityServer4 开篇讲&计划书

    前言 哈喽大家周四好!时间过的很快,现在已经是三月份了,我的 IdentityServer4 教程也拖了一定的时间了,正好最近有精力学新东西了,主要中间被小伙伴要求写一个管理后台,目前1.0已经上线( ...