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

前言

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

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

这里先给你们提一个问题,若是一个新的项目,好比一个小的问答系统交给你的手里,PM 刚刚和你简单的讨论了下需求,下一步你打算作些什么?安全

一、根据需求,马上准备设计数据库,建表,脑中模拟场景;架构

二、根据需求,马上创建实体类(也就是model层),而后CodeFirst 生成数据库;post

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

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

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

 

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

 

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

一、领域的概念

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

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

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

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

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

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

二、如何定义一个领域

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

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

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

因此,其实一个领域就是一个咱们创建的一个解决方案,一个项目,在咱们的问答项目中,整个解决方案就是一个问答领域。

 

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

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

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

咱们定义好领域,而且划分好限界后,就开始考虑如何进行实现,这里你们想想如何设计与划分,这里就说说我本身的以前的想法:

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

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

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

 

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

 

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

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

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

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

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

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

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

 

三、隔离内核

其实上边说的可能有点儿朦胧,可是咱们应该都已经用到了,若是你看了个人上一个系列教程,你应该知道有一个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");
        }

 

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

 

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

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

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

概念仍是有点儿朦胧,那就举例来讲:

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

 

二、定义限界上下文

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

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

 

 

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

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

 

感受写到这里仍是没有写的很透彻,由于咱们尚未涉及到代码,可能经过代码的设计会比较好。

 

4、结语

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