我们是如何解决复杂系统扩展性问题的

84b976f5ebfedf53870b619a57d02991.png

背景

我们部门是中台部门,很多系统都具有平台化或者中台化系统的特点。就是一个系统需要承接整个集团不同事业部的业务流转。

所有系统有三个特点:

  1. 对接业务方多:大量的需求接入,对于研发团队需求吞吐能力会有一个比较大的要求;

  2. 业务多样化:个性化业务多,如何解决变化带来的成本问题,关乎到效率及稳定性;

  3. 不同业务发展阶段不同,迭代效率诉求不一样:怎么解决研发交付塞车、撞车的问题;

基于以上问题及挑战,我们交付了两套研发框架:

  • 一套基于微内核+插件化的研发框架,解决了业务逻辑隔离与数据隔离,实现了独立开发、交付,提高了稳定性及扩展性;

  • 一个基于Serverless的交付框架,解决了不同需求不同发布周期撞车、塞车的问题,结合本地IDE快速交付新功能上线;

低代码、可配置的方案今年可能会落地一套,主要目标是将简单、重复、低成本的需求以配置化搭建的方式由事业部的产品或者运营同学,进一步释放中台研发团队的交付压力,抽时间去做更有价值的事情。

微内核的研发框架

今天我们主要讲微内核+插件化研发框架的实现逻辑,Serverless及低代码配置化放到以后讲。

一个中台化或者平台化的架构都可以被抽象为一个微内核的架构,内核就是中台系统本应该具有的核心能力,插件就是不同业务线、不同事业部在不同阶段提出的个性化能力,也就是基础能力的扩展需求。

f319c5ed67ddd458ed99f676ad4178c3.png

通过插件化架构,我们可以封装不变的逻辑,也就是我们的核心能力,不断打磨。提供可扩展抽象的接口,也就是我们的扩展能力,应对灵活变化的需求。

扩展点定义

一个个被抽象之后,用于扩展个性化需求的接口就叫做扩展点。

对接口的入参、返回值的格式进行标准化,进而实现了对扩展点定义的约束,扩展点以Ext结尾,便于研发查找,修改。

比如一个组织结构相关的扩展点长这样:

public interface OrganizationExtPt extends ExtensionPointI {

    /**
     * 根据corpId查询企业下所有部门
     *
     * @param corpId        企业编号
     * @param includeDelete 是否包含删除的部门
     * @return 部门
     */
    List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete);
}

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Component
public @interface Extension {
    String bizId()  default BizScenario.DEFAULT_BIZ_ID;
    String useCase() default BizScenario.DEFAULT_USE_CASE;
    String scenario() default BizScenario.DEFAULT_SCENARIO;
}

我们准备对这个扩展点进行扩展实现,比如有的企业使用钉钉作为组织信息管理,有的使用企业微信进行组织信息管理。

ed2018db35f13bdf1a856fbf63c31528.png

扩展定义上参考了AKF扩展模型,就是需要基于XYZ三个角度实现一个可以真正实现正交分解的业务定义。

0558aad607b6b275385d8a3c0a45b619.png

比如采用:业务、用例、场景三个角度定义。

那么钉钉的扩展实现如下:

@Extension(bizId = "organize",useCase = "getByCorpId",scenario = "dingTalk")
@Slf4j
public class DingTalkOrganizationExt implements OrganizationExtPt {

    @Override
    public List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete) {

        log.info("在组织结构业务,通过企业编号获取部门列表的用例,在钉钉的场景下业务的实现处理方式");

        log.info("通过钉钉的配置信息和API获取得到组织信息,并组装成云枢识别的部门信息");

        Department department = new Department();

        department.setName("dingTalk");
        department.setCorpId(corpId);

        return Collections.singletonList(department);
    }
}

企业微信的扩展实现:

@Extension(bizId = "organize",useCase = "getByCorpId",scenario = "wechat")
@Slf4j
public class WechatOrganizationExt  implements OrganizationExtPt {
    @Override
    public List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete) {

        log.info("业务:组织机构,用例:通过企业编号获取部门 , 场景:企业微信");

        log.info("通过企业微信的API获取组织的部门信息,然后包装为需要的部门列表");

        Department department = new Department();

        department.setName("wechat");
        department.setCorpId(corpId);

        return Collections.singletonList(department);
    }
}

扩展点执行

当我们定义了扩展点,实现了扩展实现,那么扩展点及扩展实现是如何被执行的呢?

@Command
public class OrgazationQueryExe implements CommandExecutorI<MultiResponse, OrgnizationQry> {


    private final ExtensionExecutor extensionExecutor;

    public OrgazationQueryExe(ExtensionExecutor extensionExecutor) {
        this.extensionExecutor = extensionExecutor;
    }


    @Override
    public MultiResponse execute(OrgnizationQry cmd) {

        String corpId = cmd.getCorpId();

        boolean includeDelete = cmd.isIncludeDelete();

        List<Department> departmentList = extensionExecutor.execute(OrganizationExtPt.class, cmd.getBizScenario(),
                ex -> ex.getDepartmentsByCorpId(corpId, includeDelete));


        return MultiResponse.ofWithoutTotal(departmentList);
    }
}

ExtensionExecutor中,实现了根据不同的cmd找到不同的OrganizationExtPt.class,并进行调用。

d618547746ce572407c52d8eb2ec46e6.png

如何通过坐标得到扩展实例:

protected <Ext> Ext locateExtension(Class<Ext> targetClz, BizScenario bizScenario) {
        checkNull(bizScenario);

        Ext extension;
        String bizScenarioUniqueIdentity = bizScenario.getUniqueIdentity();
        logger.debug("BizScenario in locateExtension is : " + bizScenarioUniqueIdentity);

        // first try
        extension = firstTry(targetClz, bizScenarioUniqueIdentity);
        if (extension != null) {
            return extension;
        }

        // loop try
        extension = loopTry(targetClz, bizScenarioUniqueIdentity);
        if (extension != null) {
            return extension;
        }

        throw new ColaException("Can not find extension with ExtensionPoint: "+targetClz+" BizScenario:"+bizScenarioUniqueIdentity);
    }

a9f2625404e05fad358745f0df440768.png

以一个http调用看下如何使用。

@RestController
public class OrganizationController {

    private final OrganizationServiceI organizationServiceI;

    public OrganizationController(OrganizationServiceI organizationServiceI) {
        this.organizationServiceI = organizationServiceI;
    }

    @GetMapping(value = "/organization/getDepartmentsByCorpId/{corpId}/{scenario}")
    public MultiResponse<Department> listCustomerByName(@PathVariable("corpId") String corpId,@PathVariable("scenario") String scenario){

        OrgnizationQry qry = new OrgnizationQry();
        qry.setCorpId(corpId);
        qry.setIncludeDelete(true);
        qry.setBizScenario(BizScenario.valueOf("organize","getByCorpId",scenario));

        return organizationServiceI.getDepartmentsByCorpId(qry);
    }
}

微内核架构

一个完整的微内核架构长什么样子。

50ce6a66eb5a3f65c7c3043e29ae9319.png

基于对元数据的扩展点设计,可以灵活应对业务场景复杂多样的系统复杂度扩展问题,支持灵活的版本升级。

对于校验类、转换类需求同样可以灵活轻松扩展。

希望对你有用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值