领域驱动设计(DDD) 实践之路(一)《框架篇》

正文

本文主要介绍了基于DDD思想,在生产项目中落地应用的实践。本文为【DDD】系列文章中的第一篇,主要讲述了战略层面的DDD设计原则

 

一、DDD是什么?

DDD是什么?众里寻她千百度,蓦然回首,“DDD是一种可以借鉴的思想,而非严格遵循的方法论”.。

在日常软件开发过程中,我们不可能在不了解产品形态的前提下进行软件开发,在开发前,通常需要产品经理进行市场调研,确定产品一个大的方向框架,然后和老板们开会讨论,最后敲定初步产品模型,进行大量的业务知识梳理,而后到达软件设计的层面,最后才是开发。而在业务知识梳理的过程中,我们必然会形成某个领域知识,根据领域知识来一步步驱动软件设计,就是领域驱动设计的基本概念。

 

1、常规软件开发模式 VS 领域驱动设计模式

常规软件开发模式一般有:瀑布式敏捷式

瀑布式开发是一种传统的软件开发方法,严格遵循需求分析、产品设计、编码、测试、维护等步骤顺序,这种模式下,开发人员很难从用户那里得到反馈,默认了产品设计的这个业务模型就是正确的,导致自由度降低,尤其在前期需求不明确的情况下,开发人员比较被动,导致后期需求约为变更时候,难以调整或者成本较高。

后者在此基础上进行了改进,它也需要大量的分析,范围会设计到更精细的业务模块,它是小步迭代,周期性交付,那么获取客户的反馈也就比较频繁和及时。可敏捷也不能够将业务中的方方面面都考虑到,并且敏捷是拥抱变化的,大量的需求或者业务模型变更必将带来不小的维护成本,同时,对开发人员的要求也必然会更高。

DDD则不同:它像是更小粒度的迭代设计,它的最小单元是领域模型(Domain Model),所谓领域模型就是能够精确反映领域中某一知识元素的载体,这种知识的获取需要通过与领域专家(Domain Expert)进行频繁的沟通才能将专业知识转化为领域模型。领域模型无关技术,具有高度的业务抽象性,它能够精确的描述领域中的知识体系;同时它也是独立的,我们还需要学会如何让它具有表达性,让模型彼此之间建立关系,形成完整的领域架构。通常我们可以用象形图或一种通用的语言(Ubiquitous Language)去描述它们之间的关系。在此之上,我们就可以进行领域中的代码设计(Domain Code Design)。如果将软件设计比做是造一座房子,那么领域代码设计就好比是贴壁纸。前者已经将房子的蓝图框架规划好,而后者只是一个小部分的设计:如果墙纸贴错了,我们可以重来,可如果房子结构设计错了,那修复成本就比较大了。
 

2、DDD之领域模型  

当我们面向业务开发的过程中,应该首先思考领域模型而不是如何建表。

作为开发人员大部分人都听过一句话,“面试造航母、工作拧螺丝”,日常工作就是建表写增删改查。之所以有这样的认知,其根源在于其编码过程中遵循的是表驱动设计思想而非领域驱动设计。前者只能增加数据库的表数量,而后者才会形成长期的、具有业务意义的模型,这样的系统生命力才更加长久。我们也才能用工程的方法来编码,从编码转身为业务域的领域专家、业务专家。在通过软件实现一个业务系统时,建立一个领域模型是非常重要和必要的,因为领域模型具有以下特点:

  1. 领域模型是对具有某个边界的领域的一个抽象,反映了领域内用户业务需求的本质;领域模型是有边界的,只反应了我们在领域内所关注的部分;
  2. 领域模型只反映业务,和任何技术实现无关;领域模型不仅能反映领域中的一些实体概念,如货物,书本,应聘记录,地址,等;还能反映领域中的一些过程概念,如资金转账,等;
  3. 领域模型确保了我们的软件的业务逻辑都在一个模型中,都在一个地方;这样对提高软件的可维护性,业务可理解性以及可重用性方面都有很好的帮助;
  4. 领域模型能够帮助开发人员相对平滑地将领域知识转化为软件构造;
  5. 领域模型贯穿软件分析、设计,以及开发的整个过程;领域专家、设计人员、开发人员通过领域模型进行交流,彼此共享知识与信息;因为大家面向的都是同一个模型,所以可以防止需求走样,可以让软件设计开发人员做出来的软件真正满足需求;
  6. 要建立正确的领域模型并不简单,需要领域专家、设计、开发人员积极沟通共同努力,然后才能使大家对领域的认识不断深入,从而不断细化和完善领域模型;
  7. 为了让领域模型看的见,我们需要用一些方法来表示它;图是表达领域模型最常用的方式,但不是唯一的表达方式,代码或文字描述也能表达领域模型;
  8. 领域模型是整个软件的核心,是软件中最有价值和最具竞争力的部分;设计足够精良且符合业务需求的领域模型能够更快速的响应需求变化;

3、DDD之通用语言 

由开发人员和领域专家通力合作开发出一个领域的模型是绝对需要的,但通常会由于一些基础交流的障碍而存在难点。开发人员满脑子都是类、方法、算法、模式、架构,等等,总是想将实际生活中的概念和程序工件进行对应。但是产品经理或领域专家不关心实现细节,只用业务的术语来交谈,不同的开发人员可能理解存在偏差,这样很难保证最后项目的质量。

领域驱动设计的一个核心的原则是使用一种基于模型的语言。在事件风暴过程中,通过团队交流达成共识的,能够简单、清晰、准确描述业务涵义和规则的语言就是通用语言。也就是说,通用语言是团队统一的语言,不管你在团队中承担什么角色,在同一个领域的软件生命周期里都使用统一的语言进行交流。,这种语言被称为“通用语言(Ubiquitous Language)”。通用语言应该在建模过程中广泛尝试以推动软件专家和领域专家之间的沟通,从而发现要在模型中使用的主要的领域概念。

在事件风暴的过程中,领域专家会和设计、开发人员一起建立领域模型,在领域建模的过程中会形成通用的业务术语和用户故事。事件风暴也是一个项目团队统一语言的过程。通过用户故事分析会形成一个个的领域对象,这些领域对象对应领域模型的业务对象,每一个业务对象和领域对象都有通用的名词术语,并且一一映射。微服务代码模型来源于领域模型,每个代码模型的代码对象跟领域对象一一对应。通用语言确定了项目团队内部交流的统一语言,而这个语言所在的语义环境则是由限界上下文来限定的,以确保语义的唯一性。

4、DDD之思考问题的角度

建立领域模型时也要将用户置于模型之外

  1. 我们设计领域模型时不能以用户为中心作为出发点去思考问题,不能老是想着用户会对系统做什么;而应该从一个客观的角度,根据用户需求挖掘出领域内的相关事物,思考这些事物的本质关联及其变化规律作为出发点去思考问题。
  2. 领域模型是排除了人之外的客观世界模型,但是领域模型包含人所扮演的参与者角色,但是一般情况下不要让参与者角色在领域模型中占据主要位置,如果以人所扮演的参与者角色在领域模型中占据主要位置,那么各个系统的领域模型将变得没有差别,因为软件系统就是一个人机交互的系统,都是以人为主的活动记录或跟踪;因此,当我们谈及领域模型时,已经默认把人的因素排除开了,因为领域只有对人来说才有意义,人是在领域范围之外的,如果人也划入领域,领域模型将很难保持客观性。领域模型是与谁用和怎样用是无关的客观模型。归纳起来说就是,领域建模是建立虚拟模型让我们现实的人使用,而不是建立虚拟空间,去模仿现实。

5、DDD之分层架构

在《实现领域驱动设计》书中提到,DDD 分层架构有一个重要的依赖原则:“每层只 能与位于其下方的层发生耦合。

我们常规项目中的三层架构模型,大家都很熟悉,从上图可以看出,三层架构向DDD四层架构演进的主要变化,在于逻辑层和数据访问层。

我们先来看一下业务逻辑层的变化。DDD 分层架构对三层架构的业务逻辑层进行了更 清晰的划分,改善了三层架构核心业务逻辑混乱、代码改动相互影响大的问题。DDD 分层 架构将三层架构业务逻辑层的业务逻辑拆分到了应用层和领域层,分别以应用服务和领域服务等形式存在。应用服务实现服务的组合和编排,领域服务完成核心领域逻辑,应用服 务可以快速响应前端业务和流程的变化,而领域层则更加专注领域模型和实现领域逻辑。

再来看一下数据访问层的变化。这个变化主要发生在数据访问层和基础层之间。三层架构数据访问采用 DAO 方式,而 DDD 分层架构对数据库等基础资源访问时采用了 仓储设计模式,领域层可以通过仓储接口访问基础资源的实现逻辑。这样,通过依赖倒置 实现了各层对基础资源的解耦。原来三层架构的第三方工具包、驱动、Common、Utility、Config 等通用的、公共的基础资源统一放到了基础层。

另外,DDD 分层架构在用户接口层引入了 DTO 和 facade 接口,可以给前端应用提供 更灵活的数据和接口适配能力。

用户界面/展现层

负责向用户展现信息以及解释用户命令。更细的方面来讲就是:

  1. 请求应用层以获取用户所需要展现的数据;
  2. 发送命令给应用层要求其执行某个用户命令;

应用层

很薄的一层,定义软件要完成的所有任务。对外为展现层提供各种应用功能(包括查询或命令),对内调用领域层(领域对象或领域服务)完成各种业务逻辑,应用层不包含业务逻辑。

领域层

负责表达业务概念,业务状态信息以及业务规则,领域模型处于这一层,是业务软件的核心。

基础设施层

本层为其他层提供通用的技术能力;提供了层间的通信;为领域层实现持久化机制;总之,基础设施层可以通过架构和框架来支持其他层的技术需求;

 

二、如何DDD

DDD分为战略设计和战术设计。在战略设计中,我们讲求的是子域和限界上下文(Bounded Context,BC)的划分,以及各个限界上下文之间的上下游关系。当前如此火热的“在微服务中使用DDD”这个命题,究其最初的逻辑无外乎是“DDD中的限界上下文可以用于指导微服务中的服务划分”。事实上,限界上下文依然是软件模块化的一种体现,与我们一直以来追求的模块化原则的驱动力是相同的,即通过一定的手段使软件系统在人的大脑中更加有条理地呈现,让作为“目的”的人能够更简单地了解进而掌控软件系统。

如果说战略设计更偏向于软件架构,那么战术设计便更偏向于编码实现。DDD战术设计的目的是使得业务能够从技术中分离并突显出来,让代码直接表达业务的本身,其中包含了聚合根、应用服务、资源库、工厂等概念。虽然DDD不一定通过面向对象(OO)来实现,但是通常情况下在实践DDD时我们采用的是OO编程范式,行业中甚至有种说法是“DDD是OO进阶”,意思是面向对象中的基本原则(比如SOLID)在DDD中依然成立。

战略建模包括:界限上下文(Bounded Context)、上下文映射图(Context Mapping)。

战术建模包括:聚合(Aggregate)、实体(Entity)、值对象(Value Objects)、资源库(Repository)、领域服务(Domain Services)、领域事件(Domain Events)、模块(Modules)。

接下来我们结合界限上下文,Repository等几个关键的地方来进一步阐述,如何DDD。如何去实现,可以根据实际情况具体分析。我觉得战略DDD比战术DDD更重要,我想这就是DDD作为一种思想的神奇所在。如同金庸笔下的少林绝学易筋经一样,一套并无明确招式的内功心法却能打遍武林。

1. 界限上下文的理解

限界上下文的概念很重要,刚开始学习DDD的时候,不是很了解这个概念,只是隐约记得什么限定上下文、是实体、值对象和领域服务等几个名词,其实限界上下文感觉更清楚,限的意思就是划分、规定,界就是界限、边界,上下文就是业务的整个流程,总结起来就是限界上下文是 在划定的界限中的一个业务流程,同时对于业务的描述是通过通用语言来表述的,限界上下文和通用语言的关系就是:在一个特定的限界上下文只使用一套通用语言,并且保证它的清晰性和简洁性。

首先,领域可以拆分为多个子领域。一个领域相当于一个问题域,领域拆分为子域的过程就是大问题拆分为小问题的过程。在上图中,项目被拆分为存储、订单、转写 三个子域;子域还可以根据需要进一步拆分下一级子域,比如转写域还可以拆分为识别和字幕等子域,各类子域的边界,就是限界上下文的边界了。同时,子域可能会包含多个限界上下文,比如在存储域,包括存储域、用户额度等相关行为域,分享域等。

 

每个领域模型都有它对应的限界上下文,团队在限界上下文内用通用语言交流。领域内所有限界上下文的领域模型构成整个领域的领域模型。理论上限界上下文就是微服务的边界。我们将限界上下文内的领域模型映射到微服务,就完成了从问题域到软件的解决方案。可以说,限界上下文是微服务设计和拆分的主要依据。在领域模型中,如果不考虑技术异构、团队沟通等其它外部因素,一个限界上下文理论上就可以设计为一个微服务。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值