信也科技在近年的金融科技转型过程中,助贷业务对接了大量的第三方金融机构, 大部分场景中信也科技作为对接的服务调用方调用第三方金融机构的接口。
在对接过程中,就算是相同的一个业务场景,由于各家机构提供的接口在设计上差异性很大(字段、调用方式、 加解密等),对接开发人员往往要对每家机构的对接部分做大量定制化开发工作, 这些工作既包括特制化的对接开发业务实现,又包括大量功能性代码的开发(如SFTP文件上传下载等,相关业务表的更新等),使得开发既费时费力,又让代码中充斥着大量重复的模板代码块,对系统持续稳定性和自动化测试也是不小的挑战。
为了让对接机构变得更加高效和简单,让相关系统更稳定和可测试,我们的系统一直朝着所谓“低代码快速对接平台”演进。
低代码开发平台(LCDP)英文全称为Low-Code Development Platform, 无需编码(0代码)或通过少量代码就可以快速完成开发的开发平台,让不仅是具有专业编程能力的开发人员,非技术背景的业务人员同样可以完成开发工作。
下图为典型的和第三方机构交互的例子:
一开始对接的机构较少,所以机构对接逻辑是直接在业务系统中实现并通过约定接口直接调用到第三方机构的系统。该版本主要问题:
机构对接逻辑还是直接在业务系统中实现,不过将对第三方接口的调用逻辑抽象到公共的业务网关中,实现对接口调用的统一化管理。
所以该版本的优化主要体现在对出口调用的收口上,让对接人员不用再关注网关层面相关实现。该版本主要问题:
将不同的接口按业务领域划分成不同的流程节点,业务系统内部实现了一套特定的流程引擎,每家机构实现自己的流程编排,由流程引擎内部维护流程节点间的流转状态并按流程编排发起调度。
所以该版本的优化主要体现在对流程领域的划分和收口不同节点间的调度,让对接人员按照不同节点分别对接同时不用再实现节点间复杂的流转调度。该版本主要问题:
在平台整体架构的持续迭代演进中,系统的可控性、自动化程度、对接易用性、可扩展性等指标乃至于机构对接人效的大大提升有赖于以下几点关键路径的实现。
业务网关除了对调用出口统一管理并按机构分发等功能外,最主要的作用是通过对信也请求报文和机构返回响应报文的封装,提供给信也业务系统统一请求响应模型。
参数名 | 类型 | 说明 |
---|---|---|
applicationId | String | 应用id |
institutionId | Integer | 机构id |
requestParams | String(json) | 业务请求报文json |
requestTypeCode | String | 请求类型Code |
businessId | String | 请求业务id |
outServiceCode | String | 外部接口Code |
参数名 | 类型 | 说明 |
---|---|---|
resultCode | String | 接口请求响应码 |
resultDesc | String | 接口请求响应描述 |
content | String(json) | 机构响应报文json |
dealResultCode | String | 业务响应结果Code |
dealResultDesc | String | 业务响应结果描述 |
/**
* 业务网关报文转换接口
*/
public interface GwHttpMessageConverter {
/**
* 处理请求参数报文
* @param configDTO 配置及上下文
*/
public void formatRequest(ConfigDTO configDTO);
/**
* 处理返回结果报文
* @param configDTO 配置及上下文
* @param responseMsg 机构返回原始响应报文
* @return 处理后的报文
*/
public String formatResponse(ConfigDTO configDTO, String responseMsg);
}
同一业务场景下不同机构的交互行为其实是有相似性的,一组行为的协作和调度过程组成了该机构一个业务场景的业务流程。不过其协作和调度逻辑往往耦合在每家机构的对接实现的业务代码中,实现和维护成本都很高。因此为了解耦流程控制逻辑和业务逻辑,也为了提高流程的复用性和扩展性,我们专门针对放款这一业务场景抽象出了一个做流程定义和控制的内部流程引擎。
【流程定义】由多个【流程步骤】组成,每个步骤中包括服务调用、服务结果处理,然后根据流程流转决定后续步骤。
流程编排模块:让业务注册流程定义,注册在程序内存中
流程服务模块:提供创建、执行流程的能力
延时触发模块:负责维护定时缓冲区,写入并轮序需要定时触发的流程
流程补偿模块:负责触发长时间未被执行的流程
对接平台的配置按主要使用方的维度,大致可以分为四类:
对接平台在历史的演进中累积了大量的配置项(几千个),而这些配置基本都关联到某家机构,其不单单是一般意义上的key-value配置,而更应看成是和具体一家机构相关的业务规则元数据,所以信也内部一直使用Apollo配置系统并不十分符合对接平台的业务场景,而之前对接平台自己的配置系统存在配置项分散,关联性不强,管理困难等缺点。
魔方入驻系统因此应运而生,它以要对接(入驻)的一家机构为纵向维度,以使用方和业务模块为横向维度对所有的配置项进行归类,大大增加了配置项的关联性和可管理性,同时在此基础上对对应模块的代码逻辑进行抽象和重构,将大量可通过配置的逻辑配置化,达到对接代码“瘦身”的目标。
一般一次外部接口的调用过程,不外乎以下几个步骤:拼装请求参数、发起调用、处理返回结果,有时候还需要在拼装请求参数前做下调用前的准入校验。所以接口对接时,我们往往需要将上面的逻辑一次次重复开发实现。
那么能不能将这个过程变得更加精简,更加自动化呢?
于是我们采用了交互模板+钩子函数的办法。将前置校验、拼装请求参数、处理返回结果抽象成钩子函数和相应的默认实现,如果在机构实现系统中没有对应机构的钩子函数实现则使用模板中的默认实现。钩子函数和默认实现填充在交互模板中,由内部流程引擎对相应节点的交互模板进行调用。
这样的设计下交互模板中的默认实现覆盖了大部分机构实现场景,不用每家机构定制开发;而钩子函数则提供了无法使用默认实现时,针对该机构特殊对接实现的可能。
调用流程
关键代码实现
* 平台服务和机构实现交互适配服务钩子接口
*/
public interface AdapterService {
/**
* 接口交互全实现钩子
* @param adapterModel 适配模型
* @param <T> 交互结果
* @return
*/
<T> T doAll(AdapterModel adapterModel);
/**
* 验证有哪些钩子实现
* @param adapterModel 适配模型
* @return
*/
CheckTemplateResult checkTemplateExtends(AdapterModel adapterModel);
/**
* 前置校验及处理钩子
* @param adapterModel 适配模型
* @param <T>
* @return <校验是否通过,交互结果>
*/
<T> Pair<Boolean, T> beforeCheck(AdapterModel adapterModel);
/**
* 响应结果处理钩子
* @param adapterModel 适配模型
* @param response 网关返回结果
* @param <T> 交互结果
* @return
*/
<T> T handleResult(AdapterModel adapterModel, GwResponse response);
/**
* 请求参数处理钩子
* @param adapterModel 适配模型
* @param requestJson 请求参数
* @return 网关请求参数
*/
GwRequest handleRequestParams(AdapterModel adapterModel, JSONObject requestJson);
}
接口对接中参数字段的拼装映射往往是一个“苦力劳动”,请求参数拼装基本是一堆setA(getA())这类的代码。而且每个机构每个接口的参数字段往往各不相同,每个接口对接都需要做一遍字段拼装,麻烦且费力。
可不可以让字段的拼装映射自动化或配置化起来?
请求字段映射过程可以抽象成三步:
所以字段映射的自动化也从这几个方面实现:
文件的处理是一直都是机构对接开发中的痛点,业务场景中一份文件的生命周期往往由多步复杂的处理逻辑组成,比如文件生成、文件上传、文件下载、文件签章、文件存证等等,而这些步骤往往还需要按发起方区分我方和机构方,同时不同的机构处理文件类型不同,处理流程也完全不同,甚至打包方式传输方式传输时机都不一样。
以下是贷前业务一个复杂场景的文件处理流程例子:之前贷前对接文件的做法是简单的将所有文件处理逻辑按业务处理时机拆分成前置(授信前)文件处理和后置(放款后)文件处理两个部分,所有对接逻辑统统实现在两个部分中,往往对接实现代码中既包括大量业务逻辑实现,又包含很多非业务类功能代码如上传下载压缩删除等等,造成文件相关对接工作量大还不好维护。
那么能不能做到只发送相关指令和提供关键业务参数就让文件自动处理?
比如只发送“我方下载”的指令以及提供文件类型和对方SFTP服务器配置就让服务自动完成下载。
我们构造了一个文件处理引擎来完成对文件的处理工作,文件命令执行器FileActionExecutor负责接收文件处理命令FileAction, 通过一系列文件命令拦截器FileInterceptor执行文件流程状态等公共逻辑,最后由命令执行器FileActionHandler执行其命令相关文件处理逻辑。
* 文件流程步骤执行器
*/
@Component
public class FileActionExecutor {
//构造步骤拦截器链
private FileActionInterceptor first;
public void initInterceptorChain() {
first = new FileActionPreInterceptor(new FileActionExtendsInterceptor(new FileFlowRecordOperationInterceptor(new FileActionInvoker())));
}
public InstFileResult execute(FileActionContext fileActionContext, FileAction fileAction) {
return first.execute(fileActionContext, fileAction);
}
}
/**
* 文件步骤命令拦截器接口
*/
public interface FileActionInterceptor {
InstFileResult execute(FileActionContext fileActionContext, FileAction action);
FileActionInterceptor getNext();
void setNext(FileActionInterceptor next);
}
/**
* 文件步骤命令接口
*/
public interface FileAction {
InstFileResult doAction(FileActionContext fileActionContext) throws Exception;
}
/**
* 文件步骤命令处理器接口
*/
public interface FileActionHandler {
InstFileResult onAction(FileActionContext fileActionContext) throws Exception;
}
经过文件处理引擎的处理,文件的非功能逻辑和处理步骤间的流转等不再需要对接开发人员实现,只需要通过文件相关配置和少量特制代码来完成机构相关文件部分实现,在结构上重构优化了原来的文件处理逻辑同时极大的提高了接入效率。
信也科技机构资金低代码对接平台的变化过程很好的印证了“一个好的架构不是设计出来的,而是演进出来的”这一经验之谈,架构的调整往往正是为了解决业务场景的下的某些问题。
对接部分低代码化的背后其实是公共服务和公共逻辑的更加复杂化。整个过程中如何平衡公共服务部分和对接实现间的复杂性易用性,复用点和扩展点如何选择,业务方和技术方诉求如何求同存异,既是挑战也是团队一直以来重点的思考因素。
在适配不同机构差异化进一步提高对接效率的同时,平台对外方面我们自己的标准化对接合作模式正在稳步推进中,未来希望能用自己的技术和经验提供给合作方更好的帮助和服务。同时平台对内方面考虑将已有的公共服务做更进一步的下沉提炼出一些有价值的能力以面向更多的系统和应用。
xieshuning, 信也科技业务中台团队业务架构组资深技术专家,目前主要负责资金端相关业务平台的设计和研发工作。