程序的扩展性与中台化建设

1,896 阅读14分钟

引言

目前很多互联网公司都在搭建自己的业务中台,那么为什要搭建中台?中台化有什么好处?怎么搭建中台?中台化是一个综合性的方法论,本文从程序的扩展性开始,逐步讨论烟囱式开发以及中台化建设涉及的各方面知识。

1. 封装性API&扩展性SPI

API体现的是封装性,已有功能像个黑盒子,从外部在已经的基础上扩展;SPI是专为扩展性而设计,可以从系统内部加强系统的功能。

  • API:使用者直接使用功能,不用关心怎么实现的;直接面对的是消费者;

  • SPI:使用者需要自己扩展开发,实现系统提供的标准接口定义;面对的是系统的共建生产者;

API就像是投影仪或显示器,使用HDMI线连接上就可以观看视频;而SPI就像USB标准,不同的厂商可以生产复合USB标准的鼠标或键盘等设备。总结:API是提供给客户直接使用的产品功能,而SPI是提供给生产商的生产标准;在程序中,API是编程接口,调用方直接调用;SPI是服务编程接口,是给第三方扩展开发用的标准,如下图所示:

API示意图

SPI示意图

为什么要通过SPI共建呢?有没有其他选择?为什么不直接使用编程语言本身提供的扩展能力直接扩展开发呢?

2. 编程语言的扩展设计 VS SPI的扩展性设计

什么都行,很多时候等于什么都不行,设计的本质并不是强化能力,更多的是限制能力。

换句话说:设计不是告诉你能做什么,而是告诉你不能做什么。

换句话说:设计不是加上这个功能是否可以,而是删除掉可不可以。

2.1 编程语言强大的扩展性

面向对象编程语言提供了扩展性,比如java,自身就提供了interface和Override机制。子类可以通过实现接口或继承父类,来扩展原来程序的功能,比如下面的简单继承的示例,鸟可以继承动物,并且重新移动的方法。

public class Animal {
    public void move() {
        System.out.println("动物可以行动");
    }
}
public class Bird extends Animal {
    @Override
    public void move() {
        System.out.println("鸟可以飞");
    }
}

在我们使用spring或mybatis等这些开源框架的时候,大部分场景都是直接使用框架功能(API)。如果我们对框架做出很多扩展,如下图,框架升级的时候很难保证所有的接口和类不发生改变。发生升级后,我们会发现原来的扩展开发都无法匹配使用了,原来的扩展开发代码资产全部要重新开发。

2.2 使用SPI限制扩展范围

减少承诺,更好的遵守承诺

但对于一个支持二次开发的平台,我们必须尽力保护好客户已经扩展开发形成的数据资产。保护意味着不变性,程序总是要不断迭代,迭代意味着可变形,解决这种冲突的办法就是「分离变与不变」:

  • 不变的承诺就是SPI,这些SPI可以进行一些通用化设计考量,后续尽量不变,需要改变尽量兼容。
  • 程序其他部分外部不可进行直接扩展二次开发,程序可以根据自身诉求进行迭代,可以进行面向对象的更好维护的设计。

如上图:平台代码分离开「可扩展部分」和「不可扩展部分」。「不可扩展部分」伴随框架升级可以随便修改(第三方扩展开发无感知) ;「可扩展部分」尽量稳定提供历史接口兼容,第三方扩展开发不会受到影响 。在保护第三方扩展开发的资产和框架自身的可持续升级之间取得平衡。 「可扩展部分的接口」既「SPI」第三方可以扩展,官方也可以提供实现了这些接口的「官方组件」,第三方可以直接使用官方组件。

通用性设计与面向对象设计:

使用面向对象的语言我们一般采用面向对象进行设计和编写程序,很多习惯面向对象的研发都对结构化的编程,比如MAP来表示对象数据非常的反对,其实结构化和面向对象并无优劣之分,只是适用场景不同,MAP是一种通用结构,可以更好的保证不变型,在设计SPI的很多时候作为入参或出参的扩展字段出现。

2.3 扩展性与中台化的关系

以上为「扩展性和SPI」相关的讨论,接下来会展开「中台化建设」的讨论,核心原理基本都在「扩展性」里讨论了,你可以认为下面的「中台化建设」只是上面「扩展性」的一个使用示例的场景。

3 中台化建设

3.1 中台化的概念

国内的中台化是阿里首先提出的概念,目的是为了共建,共建是基于上文的「SPI扩展开发」实现。但中台化范畴不只是解决共建的问题,最核心的是业务资产的沉淀,业务资产需要通过「DDD」来分析建模;核心资产和非核心资产可以通过「六边形架构」进行剥离;中台化之所以在互联网行业先提出,有其必然性,因为互联网初期发展太快,为了快速快速的需求迭代,初期都是采用烟囱式,每个业务都是有独立的部门开发,伴随行业和业务逐渐稳定,互联网进入下半场,如何降本增效成为主要目标,能能够去除重复开发的中台化应运而生。

3.2 烟筒式独立开发

烟囱式开发顾名思义,如下图每个部门独立开发,不同部门就像是一个个独立的烟囱,独立冒烟(开发),互不耦合。好处显而易见:每个业务部门都有专门的技术团队进行开发,不需要跨部门协作,新需求可以快速迭代开发上线,迅速占领市场。(下图是个电商示例:一个电商平台存在国内线上业务,国内门店业务,以及国外业务,为了国内和海外业务需求互不影响,不同业务有不同的开发团队:)

  • 优点:互不影响,可以快速响应需求,管理成本低。
  • 缺点:存在大量的重复开发,相同的逻辑不同团队需要重复开发一遍,如下图所示(不同垂直业务林立,形似烟筒称为烟筒式开发)。

问:重复一定不好吗? 答:并不一定,存在即合理,烟囱式开发的优点在上文说过,每个团队都可以快速响应业务,在市场开拓阶段,烟囱式开发是非常合适的,不同方式和阶段有关。1.市场开拓阶段,需求很多是开创行的,老的系统赋能不一定能满足,反而会制约新业务的发展。2.在业务初期,业务形态并不成熟,也不适合沉淀复用,也许还没沉淀好,业务就已经改变了。所以在创新业务和新业务形态,烟囱式开发是非常合适的。 对应的中台化的业务资产沉淀,也是有适用场景的,适用于业务形态成熟的业务领域,有相互的行业标准更好,有标准意味着更稳定,但就算是成熟的业务领域,这些业务资产沉淀也是具有两面性的,如果业务形态发生颠覆性的变化,需要及时识别,改变开发模式,以免复用的好处没有享受到,反而是个制约。

3.3 中台化思路-沉淀业务资产

伴随业务的逐渐发展和行业的成熟,不同业务条线的差异化逐渐变小,公共逻辑越来越多。并且当需要用现有的成熟能力开拓新的市场,按照之前的方式还需要成立单独研发部门,招聘大量研发人员。此时迫切希望能够直接把现有的成熟能力直接赋能新的业务,形成如下的结构:通用的电商中台做了所有条线的共同的逻辑,每个业务条线只在中台的基础上扩展开发开发自己特有的能力。如果需要使用现有能力直接赋能新业务,直接拿下面的中台进行支撑就可以。

前台与中台的定义与常见歧义

这里的前台不是指页面前台,前台指的是不同的业务条线,中台指的是不同业务条线的公共业务;拿阿里来说,天猫和淘宝是两个前台部门,中台部门是维护天猫与淘宝公共的业务逻辑的部门。

中台重沉淀积累,前台站在中台的沉淀基础上,快速响应业务。

前台别名:垂直方,借鉴烟囱式开发的寓意,烟囱式垂直的。

中台别名:水平方,垂直的反义词,水平的地平线可以支撑起来不同的烟囱。

以上说明了中台化演进的前因后果,接下来说下技术上如何搭建一个中台系统。

4. 中台的实施过程

4.1 中台的树形组件结构

通过SPI定义扩展点,程序框架进行扩展点调用,扩展点的实现有中台开发的公共水平组件,也有前台垂直方开发的个性组件。形成下图的树形结构:扩展点SPI通过引入不同垂直方共建,使不同的需求可以独立开发(甚至外包出去),提高需求开发的并行度。扩展点的需求开发人员不需要了解全局,只需要了解扩展点的定义和自己要实现的逻辑就可以。

中台化-扩展点与组件.png

4.2 代码模块划分

不同的公司虽然对于代码模块的命名和结构不尽相同,但基本原理是不会变的。

  • 六边形架构:领域服务层和领域服务释放的扩展点模块是程序的核心,众星捧月般的被所有其他模块依赖,承担了大脑的角色。
  • SPI扩展:领域服务释放扩展点定义,扩展点定义一般为独立模块,共建方只需要依赖扩展点的JAR包,就可以进行二次扩展开发。
  • SPI扩展实现:官方提供一些公共实现,不同的垂直方可以共建个性逻辑,个性逻辑发展为公共逻辑的时候,可以演变为可以复用的业务资产。
  • 基础设施层(依赖反转和六边形架构):核心服务定义基础设施SPI定义,六边形外的基础设施实现这些SPI,比如可以通过数据库或大数据存储数据,核心服务并不关心如何实现,只定义标准。

image-20220715103945409.png

下面给出一个代码参考,不同的场景可以根据自己需要进行变体:

  • 「SDK模块」:存放可扩展编程接口SPI,被垂直方以及中台代码所依赖,对应上图的扩展点extensiionSPI模块。
  • 「水平包」:依赖SPI,编写不同业务的公共组件,这些组件可以被不同业务直接使用。
  • 「垂直方」:依赖SPI,编写自己的个性逻辑,这些组件不能直接执行,需要被中台的执行框架回调。
  • 「Core」:提供程序执行的框架,装配Bean工厂(比如通过Spring注入相关扩展SPI的水平包公共组件,或垂直方个性组件),调用扩展点实现具体的领域逻辑。

image-20220822113649166.png

上面的结构突出了业务扩展SPI和技术扩展SPI,如果我们只重视业务扩展性,可以如下图做一些简化

  • 基础设施的SPI定义直接放入领域层,不再独立模块,基础设施的实现直接依赖领域层
  • 业务SPI扩展依然独立模块,被垂直方依赖,水平实现可以直接放入领域层,也可以独立模块。

image-20220822113836665.png

5.组件的组装与编排

5.1 业务场景与组件编排诉求

有了扩展点SPI和组件,在不同的业务场景中会用到不同的组件,甚至是不同组件的列表,比如在场景一执行1,3,5,8,场景二执行2,3,5,8,如下图,不同的场景我们称为「业务身份」,当然你也可以叫其他名字,目前没有特别正式的行业术语。

如上图:要解决两个问题

  • 你是谁:识别当前的场景或业务身份,一般通过模型参数进行逻辑计算得到,也可以通过表达式引擎(业务身份=模型+表达式)
  • 你可以做什么:调用不同的组件或组件列表,业务身份和组件列表有映射关系,找到对应的组件或组件列表,进行调度(类似责任链或Filter模式)

5.2 业务身份的解析

业务身份或场景一般是根据模型的某些属性已经运行环境的信息运算得来,知道你是谁,如下图所示:业务身份表达式为(租户1&(商品类型==图书)),可以用代码或表达式引擎来实现。

6. 多租户

一个系统赋能不同客户,不同客户的数据需要隔离开,最简单的办法是独立部署。如果我们使用一套部署环境满足不同客户,就需要用租户进行隔离数据,多租户环境可以更有效的利用资源,并且可以减少维护成本。如下图,物理上是一套资源,但在每个租户看起来,自己都拥有一份独立的功能和数据。

image-20220715162011773.png

数据库资源隔离

实现方式可以通过数据库的字段加入租户ID,所有数据库的SQL都会根据租户ID做过滤。

缓存资源隔离

可以在缓存的Key加入租户ID,隔离每个租户的缓存数据。

7. DDD与中台化的关系

DDD是建模的技术,在中台化里起到了什么作用?我认为DDD起到了灵魂的作用。如果不使用DDD,只是抽取了SPI支持共建,那么只是个空有其形的架子(SPI和领域模型不符合真实的业务领域),经不住需求的考验,新需求来了会越改越乱(打补丁)。因为不管是设计SPI,还是设计程序的领域模型,我们只有成为领域专家,使用DDD进行分析,才能设计出更符合真实业务的模型,才能更好的响应需求(只有真正的颠覆性需求,才需要大改,但真正的颠覆性需求很少)。

“太阳底下没有新鲜事,发生过的事情必然重复发生!”事物改变的是表象,本质都没变。

DDD 能够更好响应新需求的原理就在此,如果我们真正认识到了事物的本质,就会发现事情并非一直在变化。

举例来说古时候人们算账用算盘,后来改用计算器,然后现在计算机软件,但算账这个事情的本质并没有改变,改变的只是形式。

再比如上课读书,以前是木头制的很重的书,后来印刷术,现在的激光打印,以及电子书,但阅读和知识传播的本质并未发生改变。

所以从软件设计来说,如果我们真正的了解了一个行业的本质,就能把需求变动带来的设计变动成本降到最低,比如我们设计一个电商系统,古代人们也要买卖商品,也要搞促销,现在只不过从线下搬到了线上,我们看起来的颠覆性,未必真的是颠覆的。

DDD之所以有用,就是因为DDD总结了一套如何去发掘领域真正的本质逻辑的一套方法,看似抽象,从具体世界到模型本身就是抽象化的过程。抽象就是去除噪音,显现本质的过程。

本文地址:CSDN掘金