关于组件化链路设计的分享

简介: 提高代码代码可读性、复用性、可扩展性,从而提高开发体验和效率是基础素养。本文分享介绍一种组件化链路设计思想的分享,用于大量产品间具有相似业务处理逻辑的场景,可提高代码的复用性。


提高代码代码可读性、复用性、可扩展性,从而提高开发体验和效率是基础素养。减少重复代码,对重复代码进行抽象、下沉,遵守设计原则,应用设计模式,都有一个共同的目的:发现变化,封装变化,提高代码的可复用性,减少需求变化影响的范围,从而使软件、系统、云服务、网站等能够可控的修改与升级,具有更长的生命周期。


一、最常见的三层架构

以我们平常接触较多的三层架构开始:biz-core-common。


在开发的过程中,从三层架构的角度考虑,最简单的认识便是对于业务逻辑,在biz层去编排处理。而对于一些与业务逻辑无关,可复用的逻辑,从业务逻辑中抽离出来,在core层进行处理。底层的模型、DAO方法、外部服务的门面等,放在common层处理。这样对于新增的业务,可以复用core层的通用方法。

这是最简单的理解与要求,在这之上,很多应用都根据自身的特点,从不同的角度用不同的方法提高代码的可复用性。本文以条码的处理逻辑为例,介绍一种组件化链路设计思想。


二、为什么要用组件化链路

可以想象这样一种场景,有一种产品,处理的逻辑很复杂,代码很长。随着业务的发展,这个产品衍生出很多其它具有类似性质,但处理链路各有差异的产品。此时,怎样去设计代码的结构,才能更好地提高代码的复用性呢?

比如我们常见的18位付款码,对于一次解码链路,就要涉及复杂的逻辑,要经历十余种业务处理阶段,还要考虑一些不影响主链路的弱依赖逻辑。而除了18位otp离线码之外,还有19位离线码、18位在线码、19位在线码、24位db在线码等等。同时又涉及发码、解码、支付token置换等逻辑,对于一个专门处理码的平台,需要定制的逻辑就有百余种。每一种码的每一个流程之间都有差异性,但也有共性,若不做特殊处理,每一种码都在biz层编排业务逻辑,复用core层的方法,其开发的复杂性也是很大的,并且随着产品的持续增多,代码中的分支会越来越多,影响理解、维护与新增。

那么如何最大化的把通用逻辑抽离出来,提高其复用性、可维护性以及可拓展性呢?组件化链路的设计是其中的一种方式。


三、组件化链路设计

3.1 组件化链路思想

对于涉及多个业务处理阶段的逻辑,若每个阶段之间有所联系,但又彼此独立,便可以把每个流程抽象成一个个节点,对于一个特定业务流程而言,每一个子流程都可作为一个节点,依次执行流程节点,传递节点参数即可。如下


当我们把所有业务处理的子流程都抽象成节点以后,每一个业务流程的处理便可以通过节点间的排列组合去完成。如


 
    
    
   
    
     
     
    
    
     
     
    
    
     
     
    
    
     
     
    
    
     
     
    
    
     
     
    
    
     
     
    
    
     
     
     
     
    
    
     
     
    
   
    
 


NodeWrapper便是每个子流程抽像出来的节点,对于18离线条码的业务处理流程barCodeOfflineOtp18ParseProcess,只需要去依次处理每个节点的业务逻辑就好了。通过这样的方式,便可以通过一个统一的流程处理方法,执行所有组件化的业务流程,如下所示。

public ProcessContext execute(ProcessContext context, ProcessModel process) {
        LoggerUtil.info(LOGGER, "开始执行流程, processName=", process.getName(), ".");
        /**
         * node处理
         */
        context = nodeProcess(context, process);
        /**
         * extension处理
         */
        extensionProcess(context, process);
        return context;
    }


通过业务上下文context保存每个节点执行的结果并向下传递,首先执行nodeProcess主链路强依赖的节点,节点的执行结果影响链路推进,而后extensionProcess执行弱依赖的节点,执行失败并不影响主流程。这样我们对于业务逻辑的处理就变为了根据传参识别业务身份->执行对应流程->返回业务执行流程上下文context。而对于业务流程的处理,都只用在xml配置一下node,便可复用现有的逻辑。

更进一步地,对于ProcessModel以及NodeWrapper,我们应该怎么设计才能承载上述的功能实现?


3.2 流程节点的具体设计

3.2.1 节点设计

ProcessModel与NodeWrapper之间的关系如下

processModel内部其实就是一堆节点,是一种一对多的关系,将强依赖的主链路节点与弱依赖节点分开,如下

public class ProcessModel extends ToString {
    
    /**
     * 流程名称
     */
    private String name;
    /**
     * 普通节点列表
     */
    private List nodes;
    /**
     * 扩展节点列表
     */
    private List extensions;


自然,节点执行的能力要交予节点自己,一个节点的内部参数如下

public class NodeWrapper {
    /**
     * 业务节点名称
     */
    private String nodeName;
    /**
     * 业务节点
     */
    private ProcessNode node;
    /**
     * 参数转化
     */
    private NodeParametersMaping nodeParametersMaping = null;
}


一个节点会有节点名称nodeName、真正的执行节点node、以及参数转换的nodeParametersMaping。每个参数的设计都有其特殊的考虑,首先是业务节点名称,这个参数有什么意义呢?其实是在业务执行的过程中,可能会有分支链路的出现,根据不同的结果,可能会跳过一些节点。此时便可以指定下一个执行节点的nodeName,实现节点跳转的功能。

对于ProcessNode来说,它才是严格意义上的执行节点。

public interface ProcessNode {
    /**
     * 入参. 每个节点发布的时候,必须定义出入参数表.
     *
     * @param params
     * @return
     */
    ProcessNodeResult process(Map params);


processNode是一个接口,每个节点给予其具体实现。此处的入参是Map params,上文已经说到,节点处理是通过context传递的,但对于每个节点而言,并不需要所有context的所有参数。所以对于下一节点执行的时候,只需通过context取出需要的部分执行即可。这引发了下一个问题,如何设计一种通用的方法,去转换参数呢,这就是NodeParametersMaping所要做的。

public class NodeParametersMaping {
    /**
     * context to node 的转化配置
     */
    private Map contextToNode = null;
    /**
     * node to context 的转化配置
     * Map<结果码,Map<结果属性,上下文目标设置属性>>
     */
    private Map nodeToContextMapping = null;
    /**
     * 配置死的静态入参 key = params的key
     */
    private Map staticParamsToNode = null;
}


这里contextToNode便是用于从context中取出某些参数,转换成节点入参的配置。nodeToContextMapping便是节点执行结果放进context的配置。那这里为什么是一个Map类型的数据呢,主要是因为节点执行可能成功可能失败,失败的结果也有很多,此处主要是根据不同的执行结果,取出相应的NodeToContextMaping,往context里填充数据。NodeToContextMaping如下

public class NodeToContextMaping {
    /**
     * Node中的result.resultCode转化成流程里的code
     */
    private int processCode = -1;
    /**
     * 后续如何处理
     */
    private NodeProcessMethod processMethod = NodeProcessMethod.nextNode();
    /**
     * Node中的result结果转化到contextdata中
     */
    private Map nodeToContext = null;
    /**
     * 操作类的
     */
    private Map contextToContext = null;
    /**
     * 静态参数设置到context中
     */
    private Map staticParamsToContext = null;
}


这里的processMethod便是用于指定下一个执行节点的,是执行下一个执行节点,还是跳转节点。nodeToContext便是将节点执行结果放进context的配置文件,不同的执行结果会有不同的配置。该配置也是事先在xml中配置实现的


   
    
     
        
        
       
        
       
        
     
    
    
     
        
       
        
       
        
     
    
   
 

以上述为例,当key=1时,说明发生错误,该节点processMethod是ERROR_NODE不需要向下执行。当key=1时,将执行结果中的terminalResult放入context中的terminalResult,并且processMethod是默认值,执行下一节点。

至此,关于节点的设计思路便结束了。通过这样的设计,节点执行的顺序、不同的分支逻辑都可以覆盖到了,并且对于新增业务逻辑,在xml中配置即可,不需要重新编排复杂的业务逻辑。此时又有一个问题,context和node间具体传递参数的逻辑是怎样实现的呢?


点击查看原文,获取更多福利!

https://developer.aliyun.com/article/1124726?utm_content=g_1000366865


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

展开阅读全文

页面更新:2024-04-27

标签:在线   组件   离线   阿里   节点   逻辑   流程   参数   代码   业务

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top