DDD领域驱动设计入门

什么是领域,描述的是制药领域、环境领域、建筑领域、金融领域等,而在领域内,各种业务规则、业务知识盛行,如何有效的把控规则的变化,应对复杂知识,有一个很关键的四字词语,分而治之。分治法在很 多场景下体现了其强大的作用力。领域本身很大,那就拆分,得到更小的领域,也即子域,如同递归调用一般,将一个复杂问题拆分单独求解, 而最终将解汇总得到复杂问题解。

怎么拆,拆成怎么样合适,依据什么拆,这些在领域驱动设计中有了一套答案,虽然领域驱动设计不是银弹, 但可以说的上是一套极好的系统方法论或称为架构设计的方法论。

领域驱动设计常以战略设计与战术设计来将整个领域展现的淋漓精致,其作用范围面向业务也面向技术,从战略角度去规划系统、划分领域。而从战 术角度则从技术层面来指导我们该如 何去设计。

 

战略设计

战略设计主要从高层俯视(上帝视角) 我们的软件系统,就如同玩即时战略游戏般,可以一览地图全貌,以此来决定我们是要进攻还是防守哪个方向,同样,在软件中我们也可以以此来划分领域,确定权重方向。

统一语言

提炼领域知识,怎么个提炼法,千万条罗马路,各有各的看家本领。像事件风暴方法,用例分析方法,用户故事,甚至是开大会,各种讨论会等, 最终目的都是提炼出领域知识,而提炼过程中,达成描述上的一致性,包括系统目标、系统范围及系统所具有的功能。

这不是领域驱动设计所独有的,但却是软件开发中所必须的,为领域专家、业务分析人员、编码人员和测试 人员等团队所有成员交流时构建统一 频道。

领域拆分

对于领域这个概念,习惯性会想到制药领域、环境领域、金融领域等这些概念,而领域本身所描述的是范围, 是如同现实世界般的复杂,无边际。 借助分治法,将问题逐级细分来降低 业务和技术复杂度,将这复杂的世界 划分出清晰的边界来,反过来控制着划分后不那么复杂的世界,也既领域拆分出细化后的子领域。

子域划分

在实际解决问题时,我们也习惯将问题拆分,而怎么拆,基于什么原则 、拆,可能会依据相关性,权重,甚至 分类原则等,对于系统而言,会从架构方面考虑,基础设施考虑等,在领域驱动设计中,更偏向基于业务拆分,降低业务复杂度,也分离技术实现的复杂度,依照业务拆分后的子领 域,本身存在权重上的差异,依照重要性和功能划分为三类,投资占比也就有所不同。

  • 核心域 :其所体现的是核心服务,是代表着产品的核心竞争 力。
  • 支撑域 :其所体现的是支撑服务,没它不行,但又达不到核心 的价值,围绕着产品内部所需 要,但又不能单独变更为第三方 
  • 通用域 :其所体现的中间件服务或第三方服务。本身可以通过现有的解决方案集成来完成的服务。

限界上下文

深入到一个子域中,又是一片小天 地,在这天地中,却又还是存在着因语义与语境上的差异,让一些概念在这子域中显得额外尴尬。在一个领域 / 子域中,我们会创建一个概念上的领域边界,在这个边界中,任何领域对象都只表示特定于该边界内部的确切含义。这样边界便称为限界上下 文。其本质上是限界+上下文,上下文(Context)其实是动态的业务流程被边界(Bounded)静态切分的产物 。

上下文识别

对于上下文的识别,没有可遵循的标 准可走,从不同的角度切入将会识别 到不同的上下文,可从张逸老师的领 域驱动设计实践中窥之一二,以业务 复杂度、管理复杂度和技术复杂度出 发,面对这三个角度去依次分析,从 业务视角、工作视角、应用视角去识 别,进而识别出准确的上下文,通过 不断的分析斟酌考虑,逐渐识别出符 合当前预期的上下文,如在实际操作 环节发觉当前上下文的设计显得不那 么合理,还可再进行变动、拆分上下文。

 但需注意的一个是,我们识别上下文 的目的是什么,是为了控制上下文, 准确的说是为了控制上下文的边界、 大小,是为了保住我们所守护的上下 文不会因过度成长变大而奔溃,亦或 因上下文过度缩减而失去价值,保证上下文内一切的稳定,上下文与上下文间交互的可用性,也或者是当我们 退出上下文时,交付出来的上下文是非常可观的,而不是一个烂摊子。

上下文映射

规划了这么多限界上下文,该如何穿 针引线将这些上下文串起来便是一个 问题了,用例场景的完整实现往往是 由多个上下文的协作完成的,怎么去 组织这些上下文,领域驱动设计提到 的几种方式及软件工程中常用模式。

  • 合作关系:一荣俱荣,一损俱损。
  • 共享内核:上下文间共享领域实体。
  • 遵奉者:下游客户顺应上游供应 方。
  • 各行其道:没有关系的关系,相 互隔离。
  • 防腐层:在下游上下文与上游间 增加一道屏障,以此来隔绝与上 游的直接交互保护下游。
  • 开放主机服务:在上游与下游上 下文间增加一道协议,以此来规 范下游对上游的集成。
  • 已发布语言:发布方上下文发布 一份包含丰富文档的信息交换语 言,消费方上下文翻译并使用。 

这些模式其本质是为了 协作 ,为了 满足用例场景下对多个限界上下文的 调用,通过上下文映射图,可以清楚 知晓运行逻辑。为了实现上下文映 射,简单讲就是如何将两个上下文连贯起来 , 常 借助的方式是诸 如 RPC、HTTP、消息队列等,依照上下文间映射类型,挑选一件趁手的工具。

分层架构 

 我们通常喜欢对各种事情归纳总结, 如文章的层次分明,如建筑结构高低 有序、疏密有致,给人一种各处所关 注的信息视角不同,而组合起来显得 如此美妙。软件中同样运用着分层来 隔离关注点,以此来隔离每层的演进 速率。 当我们考虑限界上下文时,不仅需要 去考虑其内部的领域设计,还得从其 应用边界本身考虑,限界上下文是属 于架构设计层次,主要针对的是后端 架构层次的垂直切分,按照经典 DDD 的分层结构来看,共分为如下:

  • 用户界面/展现层,负责向用户展现信息以及解释用户命令。请求应用层以获取用户所需要展现的数据;发送命令给应用层要求其执行某个用户命令。

  • Application 为应用层,用来协 调应用的活动,不包含业务逻 辑,通过编排领域模型,包括领 域对象及领域服务,使它们互相 协作。不保留业务对象的状态, 但它保有应用任务的进度状态。
  • Domain 为领域层,负责表达业 务概念,业务状态信息以及业务 规则。尽管保存业务状态的技术 细节是由基础设施层实现的,但 是反映业务情况的状态是由本层 控制并且使用的。领域层是业务 软件的核心,领域模型位于这一 层。
  • Infrastructure 为基础实施层, 提供公共的基础设施组件,如持久化机制、消息管道的读取写 入、文件服务的读取写入、调用邮件服务、对外部系统的调用等 等。 

战术设计

相比于战略设计的怎么规划,战术设计更侧重于怎么执行,详细的设计和编码。

聚合

在认识聚合前,我们得对类再次回 顾,类是作为我们开发中的最小单 元,一切以类构建,而在上下文的视 角中,聚合成了最小概念,包装了一 组高度相关的对象,上下文内以聚合 为最小单元,以此来保证聚合边界。 又将分而治之的思想融入到了限界上 下文的内部。

聚合本身是由一个或多个实体及值对 象组成,其中一个实体作为聚合根。 管理着内部关联的实体与值对象,对 外代表着聚合,外部来访者仅可通过 聚合根进行访问。 

实体

对于实体来讲,这个概念对于我们并不陌生,拥有者唯一的身份标识符, 内含属性作为该实体的静态特征,作为聚合所拥有的领域知识,拥有着与自身相关的领域行为。

值对象

对于值对象, 基础类型之延伸,既能封装基础类型,又能约束内部属性间关系,还能拥有着自身的领域行为,而与实体的区别是,没有唯一身份标识,尽管带来了持久化的一些问题,但还是存在 解决方案。

聚合划分

经统一语言与业务分析阶段,借助一 系列如事件风暴、用例分析法、名次 动词法、四色建模法等活动后,获得 了一系列相关联的对象。或可形成一 张庞大的对象关联图。

 如不考虑聚合的划分,我们依照以往 的思路便是创建一大堆表,运用三范 式或是依靠程序去保证数据的一致性不运用主外键。然后疯狂撸码, CRUD好不快活。

而随着业务的逐渐扩张,这当初的想法已有点吃力了,如同树苗逐渐成 长,枝叶也逐渐增多。借助枝干我们可以分清叶子的归属,而对象网中呢,变得错综复杂了,也就隐约有了大泥球的征兆。

应用服务

作为限界上下文对外的门户,也即是外观模式的体现。通过用例分析识别 出来的用例在此处一一对应存在着,对外提供统一接口,以此满足完整用例场景所需的功能。在应用服务内部,通过编排领域模型对象来完成用例的功能,自身并不包含领域逻辑, 但包含着应用逻辑。

领域服务

当我们考虑领域逻辑时,首先想到的 应该是实体与值对象中具有的领域逻辑,而有些场景下,实体与值对象无法承载这些领域行为,如对多个领域对象作为输入,进行计算并产出一个值对象;又或是需要将操作成集合化的聚合,如在 Supplier下需要将所 有 Order 中的单价汇总,而本身 Supplier 和 Order 是为两个聚合, 若考虑借助 Order 去完成该业务操 作,不太妥当,在此场景下,可通过 领域服务来承载着这些领域行为。

领域服务存在如下特征:

  • 执行一个显著的业务操作过程
  • 对领域对象进行转换
  • 需要使用多个聚合内的实体和值对象编排业务逻辑
  • 领域行为需要访问外部资源

虽说领域服务能够承载领域逻辑,却不能说将所有的领域逻辑都往里塞, 如此,导致领域对象贫血。只有当实体与值对象承载不住或是本身并不属 于实体或值对象的职责内时,才考虑领域服务来承载,领域服务是一种妥协的结果,并不是说领域服务越多越好。

领域事件

在软件开发中,事件早已被我们所熟 悉,一个按钮按下,产生中断事件, 一个回车,前端页面有侦听事件,在 事件风暴建模活动中,事件也是作为领域建模的突破口,事件的重要性不言而喻。其本质是发生的事实到引发了相关事情,在这其中的传递的信息便是事件的内容。就如同猫叫了,引 发着老鼠跑了,主人醒了,其中的事件便是猫叫了,而该事件是猫执行叫的动作后的结果。

在领域驱动设计中,最开始的版本中 并没有领域事件的概念,在 DDD 社 区对领域驱动设计的内容不断的充实 中,引入了领域事件。领域事件的命 名遵循英语中的“名词 + 动词过去 分词”格式,如,提交订单后发布的 OrderCreated 事件,订单完成后 OrderCompleted 事件,用以表示 我们建模的领域中发生过的一件事 情,也符合着事件本身是具有时间特征。

领域事件主要用途有:

  • 从事件角度丰富了领域模型
  • 保证聚合间的数据一致性
  • 实现事件事件溯源和 CQRS 等
  • 限界上下文间集成(发布订阅模 式)

资源库

在刚接触资源库(Repository)时,第 一反应便是这就是个DAO 层,访问数据库,但是,当接触的越久,越发认识到第一反应是错的,资源库更多的是对资源的管理, 而不仅仅是数据库中的数据,数据库可以作为资源的一部分,但不是全部,我们习惯将对外部系统的调用称对于聚合来讲,资源库的作用是负责将聚合持久化到数据库的(通常是持 久化到数据库),并且由于聚合根负责维持聚合的生命周期,也就使得应考虑仅聚合根才应该拥有资源库,这也是与 DAO 层不同的地方。 在分层设计时,考虑将资源库的抽象划分到领域层,属于领域模型对象的 一部分,如同设计防腐层的抽象网关般,资源库的抽象作为特殊的网关, 当在应用层或是领域层中操作资源库抽象时,将资源库作为管理聚合状态 的工具,可以忽视基础设施层中对资 源库的具体实现。而在考虑基础设施 层中具体实现时,可根据需要选择适 合的工具,以此来管理和操作资源。

工厂

聚合从 0 到 1 的过程,可以通过多种途径创建,一般来讲,我们开发中 常直接实例化或是反射实例化,而对种方式来创建聚合,组装聚合,在创 建过程中封装业务逻辑。

  • 聚合自身担任工厂,在聚合根中实现Factory 方法
  • 独立的 Factory 类,用于有一定复杂度的创建过程,或者创建逻辑不适合放在聚合根上
  • 借助其他聚合来创建,其他聚合 担任工厂角色
  • 借助构建者模式灵活组装聚合

聚合根的创建有多种方式,依据聚合 内掌握知识的多少与创建逻辑的需要 可灵活选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值