微盛|团队技术博客

以标签系统为例展示扩展点如何在业务需求中落地

绵羊

2023-07-05

一、业务扩展点设计

首先,通常当我们提到代码重构和优化的时候,都会说到可扩展性的代码实现,其实背后的潜台词,就是想要解决业务场景引入的复杂业务逻辑和多变的业务需求,就要考虑代码的可扩展性。
一提到可扩展性的代码,都会想到基于 Strategy Pattern 的扩展。策略模式的引入,主要有两个问题:

  1. 策略模式抽象了各种实现策略,但是需要 case by case 的分析,并不能提炼成一个通用的组件固化到框架中来。
  2. 各种策略的实现,反馈到代码侧的割裂感很严重,对于策略的测试也不是很友好

我们这里业务扩展点的设计思想并没有采用 Strategy Pattern ,而是引入了 COLA 的扩展点组件。业务扩展点,主要关键点有两个:业务身份识别能力抽象的扩展点机制

1.1. 业务场景身份识别

先来看看什么是业务身份识别,业务身份识别在我们的应用中非常重要,作为基础服务的各个应用,比如客户中心,素材中心,本身就要支撑类似商城、营销、CRM 等多个上层业务服务。
另外,作为一个 SaaS 平台系统,对于每个业务服务都天然存在多租户的概念。举个例子,对于 CRM 业务的线索导入来说,越秀地产和理财通是不同的租户,导入的规则不相同,那么它就是不同的业务方,所以传统的基于多租户(TenantId)的业务身份识别,是不能满足业务上多变的要求的,在此基础上我们引入了业务码(BizCode)来标识业务。
这样一来,我们的业务身份实际上是(BizCode,TenantId)二元组。在每一个业务身份下面,又可以有多个扩展点(ExtensionPoint),所以每一个扩展点的实现(Extension),实际上是一个三维空间中的向量。
类似于 Maven Coordinate 的概念,就是一个扩展坐标的概念(Extension Coordinate),这个坐标可以用(ExtensionPoint,BizCode,TenantId)来唯一标识。
image.png
有了业务身份这个关键抽象之后,通过身份来获取扩展实现的过程就变得水到渠成了,具体流程如下:
看起来这个思路是可行的,去除掉租户的概念,简化后的扩展点定位的方法如下图:image.png然而,在实际业务需求中,除了需要支撑多个业务,很多时候还需要对同一种业务下不同的用例的差异化支持,更甚者,是对同一个用例不同场景的差异化支持。
前者比如营销业务中的“创建员工活码”和“创建识客码”两个用例,对于标签选择组件的筛选逻辑是有差异的。
又比如“创建素材”和“更新素材”是两个用例,但是大部分的业务代码是可以复用的,只有一小部分需要差异化处理。
后者比如,营销业务中的“创建员工活码”这个用例中,活码的“使用员工”是“只有自己”和“不仅仅只是自己”两种场景,在对于标签选择组件中,标签的筛选逻辑就是不同的。

  1. 创建员工活码场景,分管用户,活码的员工是自己,看到的是“所有企业标签”+“我作为管理范围内的员工,所在的管理组,管理的业务标签”
  2. 创建员工活码场景,分管用户,活码的员工不仅仅是自己,看到的是“所有企业标签”+“我作为管理员,所在的管理组,管理的业务标签”

image.png
再深入剖析一下,“标签选择组件中,在不同的业务场景下,不同的使用用户,应该展示什么样的标签数据” 这个复杂的业务场景。
image.png
先来看看产品对于规则的细化:
image.png

为了支持这种更细粒度的业务扩展,仅仅只依靠之前的“业务身份(BizCode)”是不够的,这里需要引入 Use Case 和 Scenario 两个新的概念。

1.2 业务扩展点概念

1.2.1. 业务 (Business)

对于大型公司来说,业务通常是一个业务线主体,比如 tmall、淘宝和菜鸟就是三个不同的业务。
对于我们来说,营销、商城、CRM、素材就是四个不同的业务。

1.2.2. 用例 (Use Case)

在软件工程中,每个用例提供了一个或多个场景,该场景说明了系统是如何和最终用户或其它系统互动,也就是谁可以用系统做什么,从而获得一个明确的业务目标。

创建标签、打标签等,都是标签系统的典型用例。

1.2.3. 场景 (Scenario)

场景实际上就是用例的实例(Instance),包括用例所有的可能情况(正常的和异常的)。

比如对于“创建标签”这个用例,就有“创建企业标签”,“创建自动标签”,“创建业务标签”等多个场景。
再比如“标签选择器”这个用例,有“营销域-创建员工活码-活码员工选择自己”,“营销域-创建员工活码-活码员工选择包含他人”

1.2.4. 业务 VS 用例 VS 场景

简单来说,就是一个业务是由多个用例组成的,一个用例是由多个场景组成的。

比如上面说的标签选择器这个扩展点

1.3. 扩展点实现思路

引入“用例”+“场景” 的概念后,扩展框架就从原来只能支持到“业务身份”的扩展,现在可以支持到“业务身份”,“用例”,“场景”的三级扩展,无疑比以前要灵活的多,并且在表达和可理解性上也比以前好。理论上就可以满足我们当下的业务复杂度需求了。引入新概念后,扩展定位的方式如下图:

在新的扩展框架下,实现上图中所展示的扩展:在 marketing 这个业务下——的创建员工活码用例——的使用员工只是自己场景——的标签筛选组件进行扩展,我们只需要声明一个如下的扩展实现(Extension)就可以了。

基于 Business+Use Case+Scenario 实现的业务扩展点:
营销+创建员工活码+自己
Or 营销+创建员工活码+不仅仅是自己

二、扩展点解决了标签服务什么问题

1.不同展示规则的标签聚合到同一个接口,完成接口收敛

eg1: 不同模式不同场景下不同角色可见的业务标签(权限)

2.业务灵活编排,实现不同业务需求

eg1:新增企业标签需要执行 校验、转换、加密、入库、同步外部系统、通知二方系统
而群标签仅需要执行 校验、转换、入库、通知二方系统

eg2: 在开发企业标签数据迁移job时, 单独抽出 转换 和 入库逻辑执行,实现代码复用。
image.png
image.png

三、扩展点使用方式

1.定义扩展点

定义接口继承 ExtensionPointI,接口中定义要扩展的方法。
image.png

2.实现扩展点

实现扩展点接口,加上注解 @Extension,指定注解的坐标(bizId,useCase,scenario)
image.png

3.通过扩展点执行器执行扩展点

注入ExtensionExecutor ,执行execute 或 executVoid 方法,传入扩展点, 业务坐标 和 扩展方法
image.png

四、扩展点原理

1. 设计原理

1.1 定义与注册扩展点

服务启动时,找到所有带有 @Extension 注解的类实例,提取注解中的坐标,注册到 本地的MAP中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.alibaba.cola.extension;  

import org.springframework.stereotype.Component;

import java.lang.annotation.*;

/**
* Extension
* @author fulan.zjf 2017-11-05
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Repeatable(Extensions.class)
@Component
public @interface Extension {
String bizId() default BizScenario.DEFAULT_BIZ_ID;
String useCase() default BizScenario.DEFAULT_USE_CASE;
String scenario() default BizScenario.DEFAULT_SCENARIO;
}

image.png
image.png

1.2 执行器执行扩展点

使用框架中的执行器(ExtensionExecutor)执行,从本地MAP中取到扩展点执行业务方法
image.png
image.png
image.png
image.png

Tags: 后端

作者: 绵羊