架构设计 中台与领域驱动设计
1. 中台与领域驱动关系
其实中台与领域驱动设计没有必然联系,这里放在一起就像是Nginx与Lua的,炒饭与海天拌饭酱的关系,用Nginx不一定适用lua脚本,但是lua脚本能帮助你更好的定义nginx模块更好的发挥它的优势,没有海天炒饭也不见得味道不好。
领域驱动设计DDD也是,DDD不是万金油,不是说有了DDD所有问题都解决了,DDD只是更好应对中台,微服务的一种方式而已。艾瑞克.埃文斯都不敢保证一定会成功,书名已经说得很清楚了,《领域驱动设计:软件核心复杂性应对之道》。扯远了。
2. 中台概述
2.1 中台是什么
中台是一种经营理念
中台是一种组织形式
中台是“平台思维”的自然演进
2015 年,阿里启动了中台战略。中台这个词提了这么多年,它到底是什么?
中台是一种经营理念。从整个公司管理来说,这种经营理念就是我利用中台的思想去提升整个公司的组织效率、协同效率和运营效率。
中台是一种组织形式。中台不是说我技术业务拉到一起,画个架构图,它其实需要组织架构来保障,所以中台是一种组织形式。
中台是平台思维的自然演进。中台还未提出来的时候是什么,产品线,各个中心,再到项目。其实就是“平台+数仓”的形式对外提供服务。
2.2 中台特点
我们理解的整个中台其实有三个特点。
第一个是沉淀,除了要完成业务系统开发、业务场景的开发,做技术要有沉淀的东西,去体现出我的技术能力和价值。
第二,提升效率,这个很容易理解。中台统一对外提供通用能力避免数据隔离,各系统重复造轮子。
第三,降低创新成本,当你有了中台,你的很多能力沉淀起来了之后,你可以很方便地去做前台的业务创新。比如说有某个 App 火起来了,我们可以很快就做出一个竞对的 App,因为底层的能力都是现成的,我可以很方便地就把 App 做出来。当然,如果你不具备业务中台能力,其实没有必要一定要去提中台,业务内自己运行效率才是最高的。
消除烟囱式架构与数据隔离.
零售中台库存中心打通了个不同平台库存数据隔离特点,对外提供通用能力,快速支撑上层应用,提高了工作效率。
2.3 中台分类
我理解的中台分为三类:
- 技术中台
- 数据中台
- 业务中台
具体有什么区别于联系,其实通过通过名称也能看的出来,见名知意。
技术中台提供技术支撑,组件公共平台,数据中台进行数据处理,存储,加工,业务中台就因人而异了,为不同业务系统提供服务。
3. DDD领域驱动设计
3.1 什么是领域驱动设计
领域驱动设计的概念是2004年Evic Evans在他的著作
《Domain-Driven Design : Tackling Complexity in the Heart of Software》
(中文译名:领域驱动设计:软件核心复杂性应对之道)中提出的,从领域驱动设计提出距今已经有15年的时间。
3.2 为什么用领域驱动设计
以大家熟知的电子商务系统举例,早期的电商系统因为业务相对简单,用户量和团队规模也较小,一个单体应用就可以搞定,随着容量上升可以将单体应用进行横向扩容,比如早期的淘宝就是这样做的。拆分过程中我们可以把电商系统这个单体应用拆分成订单子系统、库存子系统、物流子系统、搜索推荐子系统等等,领域驱动设计在战略层面上的域、子域、限界上下文的划分思想和微服务的划分不谋而合。域对应一个问题空间,也就是上例中的电商系统;子域是把域这个大的问题空间拆分成若干个小的更容易解决的问题空间,也就是单体应用向微服务演进过程中划分出来的各个子系统;限界上下文是解决方案空间,每个子域对应一个或多个解决方案空间。微服务的划分是也是将一个大的问题拆分成若干个小的问题,每一个小的问题用一个或多个微服务来解决。
传统分层架构存在的问题
对于大多数开发同学来说,大部分时间都花在落地一个个微服务上,下面我们来看阿里文娱是如何结合领域驱动设计的思想将微服务进行战术落地的。
目前笔者接触过的微服务大多数都是分层架构并且在Service层与Manager层实现具体的业务逻辑,使用DO、DTO、BO、VO等进行数据传输,数据和行为基本完全隔离。这种分层结构图3是《Java开发手册》中的标准分层结构。
该规范中定义了各层的职责,其中最重要的两层Service层和Manager层是这样规范的(以下两层解释摘抄自《Java 开发手册》):
Service层
相对具体的业务逻辑服务层。
Manager层
通用业务处理层,它有如下特征:
对第三方平台封装的层,预处理返回结果及转化异常信息。
对Service层通用能力的下沉,如缓存方案、中间件通用处理。
与DAO层交互,对多个DAO的组合复用
阿里文娱早期的项目分层也基本都采用这种架构形式。上面的分层并没有问题,但是这种分层架构采用的是包的形式进行的层与层的隔离,需要每一位开发同学理解并且自觉遵守以上规范,但是在实际工作中我们发现很多同学对Service层和Manager层的区别并不是特别的清楚,即使清楚的同学大部分也并没有完全遵守手册中的规范,这种现象导致Manager层除了沉底一些通用能力以外和Service层并没有什么本质区别。
在实际的业务代码中Service层和Manager层都充斥了大量的第三方依赖,对系统的稳定性有很大的影响。每依赖一个第三方服务都要各种异常问题,这些异常处理的代码往往会和业务代码混在一起,当这种代码多了以后会使代码的可读性非常差。
阿里文娱业务的复杂度提升很快,业务迭代速度也很快, Service层和Manager层代码量迅速膨胀,业务逻辑变得越来越复杂。在这种业务场景下,大文娱引入了领域驱动设计并设计了一套完整的领域驱动模型评估与演进的解决方案来辅助开发同学将领域驱动设计的思想真正的落地。
3.3 名词术语
1. 聚合根、实体、值对象的区别?
从标识的角度:
聚合根具有全局的唯一标识,而实体只有在聚合内部有唯一的本地标识,值对象没有唯一标识,不存在这个值对象或那个值对象的说法;
从是否只读的角度:
聚合根除了唯一标识外,其他所有状态信息都理论上可变;实体是可变的;值对象是只读的;
从生命周期的角度:
聚合根有独立的生命周期,实体的生命周期从属于其所属的聚合,实体完全由其所属的聚合根负责管理维护;值对象无生命周期可言,因为只是一个值;
2. 聚合根、实体、值对象对象之间如何建立关联?
聚合根到聚合根:通过ID关联;
聚合根到其内部的实体,直接对象引用;
聚合根到值对象,直接对象引用;
实体对其他对象的引用规则:1)能引用其所属聚合内的聚合根、实体、值对象;2)能引用外部聚合根,但推荐以ID的方式关联,另外也可以关联某个外部聚合内的实体,但必须是ID关联,否则就出现同一个实体的引用被两个聚合根持有,这是不允许的,一个实体的引用只能被其所属的聚合根持有;
值对象对其他对象的引用规则:只需确保值对象是只读的即可,推荐值对象的所有属性都尽量是值对象;
3. 如何识别聚合与聚合根?
明确含义:一个Bounded Context(界定的上下文)可能包含多个聚合,每个聚合都有一个根实体,叫做聚合根;
识别顺序:先找出哪些实体可能是聚合根,再逐个分析每个聚合根的边界,即该聚合根应该聚合哪些实体或值对象;最后再划分Bounded Context;
聚合边界确定法则:根据不变性约束规则(Invariant)。不变性规则有两类:1)聚合边界内必须具有哪些信息,如果没有这些信息就不能称为一个有效的聚合;2)聚合内的某些对象的状态必须满足某个业务规则;