【210730】基于领域驱动设计理论(DDD)设计

DDD的意义

  1. 关注精简的业务模型及实现的匹配,模型是对现实的有选着性的抽象和精简。

  2. 通过模型、通用语言,来跟领域专家(业务人员)、开发人员等进行信息沟通。

  3. 通过将一个复杂的系统抽象建模和拆分成一个个子系统(模块、子域),以达到减少系统复杂度的目的,一个简单的系统、或者一个初创的系统暂时没必要,也没有意义使用DDD进行设计及构建,同时考虑人员因素。

  4. 软件是将现实世界中的事务映射为计算机可执行的代码,以期待提高我们的生产效率,DDD告诉我们如何较好的完成这样的转化,以实现较好的高内聚低耦合的原则。

名词解释

1. 领域

即业务、是相关业务所涉及到的业务知识,如飞行系统、广告行业,消费金融行业、打车行业等。

2. 模型

将现实事物进行抽象,保留所涉及系统业务关注的属性、行为,去除多余的属性和行为后的结果,比如金融行业中的用户,我们关注用户年龄、身份证信息等,不关注用户有多高,多重。

3. 通用语言(UL)

在由软件架构师、开发人员、领域专家组成的设计团队,需要有一种语言来统一它们的行为。以帮助它们创建一个模型,并使用代码来表达模型。有点类似java的中间字节码,串联起开发文本代码与可执行二进制文件。

 (a). 以业务(领域)名词为基准,开发使用相同概念。 

 (b). 对业务名词添加限制和约束,定义子域和上下文(边界)。

 (c). 随着业务的发展,创建名词库,便于后续同学理解。

 (d). 通用语言,可以以图、表、UML、草图等形式呈现出来。最重要的是让业务人员(不懂技术)、系统设计人员、开发人员(可能不懂业务)对系统(模块)划分,上下文边界达成共识。

4. 模型驱动设计

通常以面向对象的方式来实现。在领域抽象设计通用语言中,用于指导开发人员具体实现的思想。介绍如何来划分子领域,让子领域能够落地实现。

模型驱动设计

image

1. 系统分层(Layered Architecture)

任何一个复杂系统,都不可能不进行分层设计。因为人的思维习惯一次性记住不了这么多东西,需要分层简化处理。分层设计比较典型的场景应该属于TCP/IP的分层设计。

image

对到业务开发来说,通常我们的系统设计如下:

image

(a).用户界面层:负责用户侧展示与交互,绝大部分场景下,系统设计都是前后端分离,该模块交由前端处理。

(b).应用层:很薄的一层,用来完成系统流程组合(聚合),不涉及具体领域内容(逻辑)。通常是跟前端交互的最直接一层。

(c).领域层:用来完成具体领域(业务)逻辑、信息、对象处理,也是业务系统的核心所在。在该层,重业务逻辑,轻存储随着业务的复杂度提升,该层也许进一步进行模块化拆分。

(d).基础设施层:作为基础层,轻业务逻辑,重存储,完成模型的存储(db/缓存)、外部系统调用(rpc)等。

对到我以前做的金融系统分层如下:

image

网关层:对应用户界面层,完成跟前端或rpc 消费者交互。

调度层:对应应用层,完成接口流程聚合、处理MQ事件、以及部分定时脚本触发流程。

逻辑层:对应具体业务逻辑处理。

数据层:完成数据的持久化、以及rpc访问封装。

了解到广告创编服务化相关系统分层如下:

image

2. 实体(Entities)

模型分实体或值对象。实体是整个系统设计中重要的对象,该对象通常具有唯一标识符,通常也是该领域设计总的重要模型。

比如银行系统中客户(具有客户id,身份证号)、广告系统中的广告(具有广告ID)、交易系统中的订单(具有订单号)等。

3. 值对象(Value Objects)

我们不可能将所有的对象都定义成实体(每个都分配唯一标识符,成本过高),因此有些不是那么重要的,也不需要关注状态的对象,可以定义成值对象。

如一个坐标定义Point(x1,x2)、一个地址定义Address(国家、城市、街道)。

4. 服务(Service)

服务是模型设计对外暴露行为或调用的单元,服务不同于对象,通常包含一系列行为(操作)。它具备如下特点:

(a). 服务的操作包含领域概念,无法映射成一个对象。

(b). 操作无状态。

5. 模块(Module)

对于一个大型的复杂项目,模型(业务)会越来越大,每个人理解起来都会相对比较困难,根据人的思维习惯,通常需要进行系统拆分,分开理解降低系统复杂度,拆分过程中,将具有公共属性的对象、行为组装在一个模块中。模块名称需要成为通用语言理解的一部分。

如金融支付系统中的模块划分:将大的模块划分为通道接入、核心交由、资金接入。

image

广告创编服务化相关模块划分如下:

image

6. 聚合(Aggregates)

聚合,可以简单理解为对象关联,将相关联的对象聚合在一个实体(聚合根)下面,外界对该组对象的访问,都只能通过聚合根访问。下图例子比较形象:

image

消费者对象(实体、聚合根),关联了联系簿对象,和地址对象,外界访问通过消费者对象访问。

7. 工厂(Factories)

工厂被用来封装对象创建所必需的知识,创建对象可以由对象自己来封装,也可以由第三方来创建。

创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

8. 资源库(Repositories)

资源库,完成对象的持久化存储、远程RPC调用封装。

image

保持模型的一致性(各个模块/领域如何保持协作)

我们现在已经有了各个模块、子领域,如何确保不同子领域如何协作。

image

1. 界定的上下文(Bounded Context)

当我们确定一个模型的时候,我们需要确定它的范围,定出它的上下文的边界,尽最大可能保持模型的统一。上下文的界定通常可以从如下几个方面确定:

(a). 团队的组织结构。

(b). 应用的特定部分中的惯例。

(c). 物理表现(例如代码仓库、数据库、redis集群等)

2. 持续集成(Continuous Intergration)

持续集成指的是,频繁地(一天多次)将代码集成到主干。持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。

(1)快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。

(2)防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。

3. 上下文映射(Context Map)

各个模块如何进行协调的手段。不同上下文之间的交互。

(1). 共享内核(Shared Kernel)

image

有点类似于多线程操作共同变量,通过共享内存进行交互。两个不同领域模型(团队),共享部分领域模型子集(模块),任何对子集的操作都需要知会到其他共享的领域模型(团队),集成的时候,两个团队都需要开发和测试介入。

曾经跟另外一个业务团队共用了一个底层额度(功能)开发,每次涉及公共模块开发都需要知会另一个团队,这样做有些问题,沟通效率也不高,后来改成有一个团队维护公共模块,另一个团队如果有述求,提需求给维护的团队处理。涉及到下面的模式(客户-供应商)。

(2). 客户-供应商(Customer Supplier Teams)

当一个系统严重依赖于另一个子系统,两个系统的上下文是不同的,并且一个系统的处理结果被作为另外一个的输入。一个系统如果有述求,提需求给下游子系统。

(3). 顺从模式(Conformist)

上游上下文只能盲目依赖下游上下文。

通常,可以通过MQ、数据库主从复制,做到隔离。

(4). 开放主机服务(Open Host Service)

定义一种协议,让其他上下文来访问本上下文。

曾经,我们的各个业务系统(很多子领域)需要访问中台订单系统(中台服务),中台不可能为每个业务系统单独定制化访问接口,因此提炼出一套通用的标准订单查询接口(Published Language),定义好入参枚举值,出参枚举值,访问系统token等信息。各个业务系统共用一套访问接口,减少沟通、维护成本。

(5). 隔离通道(Separate Way)

当多个系统,没有任何交集的模型(上下文)的时候,这些系统可以独立拆分独立建模,无需集成,各自玩各自的。

(6). 防腐层(Anticorruption Layer)

image

上游上下文(模型)依赖于下游上下文(模型),为避免下游上下文嵌入过深,导致下游有调整,上游调整比较大,在中间,上游上下文引入适配层,隔离下游变化,访问下游系统。

4. 提炼(找到重心)

即使在我们改进和创建很多抽象之后,一个大的领域还是会有一个很大模型,我们需要进行提炼和子域划分,在子域中,提炼出一个核心域(Core Domain),若干个普通子域(Generic Subdomain)。

核心域需投入较多的精力进行优化和迭代。

对于普通域来说,有如下几种方式:

(1). 购买现成的解决方案

这个方法的好处是可以使用别人已经完成的全套解决方案。随之而来的是学习曲线的问题,而且这样的方案还会引入一些依赖。如果代码有很多 bug,你只得等待别人来解决。

(2). 外包

将设计和实现交给另外一个团队,有可能是其他公司的团队。这样做可以使你专注于核心域,不再承受处理另一个领域的负担。不便的地方是集成外包的代码。

(3). 已有模型

一个取巧的方案是使用一个已经创建的模型。市面上已经有一些关于分析模式的书,可以用来作为我们子域的灵感来源。

(4). 自己实现

这个方案的好处是能够做到最好的集成,但这也意味着额外的付出,包括维护的压力等。

DDD来讲述软件设计的术与器,本质是为了高内聚低耦合。

关于DDD的思考与疑问

  1. 领域驱动设计,是先有业务(领域),再有设计。针对未知的环境或领域,是否还合适(比如创业团队)?

  2. 重构,随着业务的发展需要持续进行?

参考

领域驱动设计精简版(全新修订):https://www.infoq.cn/article/domain-driven-design-quickly-new

阿里技术专家详解DDD系列 第二弹 - 应用架构:https://mp.weixin.qq.com/s/MU1rqpQ1aA1p7OtXqVVwxQ

领域驱动设计在美团点评业务系统的实践:https://mp.weixin.qq.com/s/uZmrZ61jB2JERCqHNDqktQ

重读领域驱动设计——如何说好一门通用语言:https://zhuanlan.zhihu.com/p/62959217

23种设计模式汇总整理:https://blog.csdn.net/jason0539/article/details/44956775

持续集成是什么?:http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html

你可能感兴趣的:(【210730】基于领域驱动设计理论(DDD)设计)