# 聊聊最近一次对公司的SCRM产品的重构
背景:公司去年上线了款基于企微的SCRM产品,效果不是很好,服务商来投诉了很多次。
加上最近企微的接口有比较大的更新,且不兼容旧版本接口,公司没有安排人去处理,导致新租户已经部分功能不可用了。
# 先列举一下主要问题:
# 技术债问题:
- 设计缺陷
- 过度设计
- 横向扩展性缺失
- 架构风格不统一
- 编码问题
- 线程、http客户端等组件使用不当,导致trace传递失败。导致问题排查效率低
- 企微接口升级
- 接口适配
- 数据转换
# 产品问题:
- 实现方案复杂,各种方案的组合都可能有正式使用的租户。各种组合的情况有3 * 2 * 3=18种
- 实施方案过多,有:原生开发、低代码平台1.0、低代码平台2.0
- 对接企微的方式过多,有:自建应用+第三方应用、代开发应用+第三方应用
- 对接企微的域名方案过多,有:统一域名、租户特有域名
- 实施流程问题
- 实施步骤冗余:装第三方应用>
- 实施流程交互不合理
- 操作错误无反馈
- 限制配置无法更新,导致配置错误后无法在界面更改
- 需要到第三方页面生成公私钥再填到系统及企微
# 以及解决的思路:
时间有限,即使开发资源再多也没法缩短总工期。而且人多,沟通成本、冲突合并的解决成本也会随之上升。
此时减少开发、测试的工作量,使各个开发、测试人员尽量的并行工作,成为了关键。
# 减少产品设计问题带来的复杂度
# 实现方案简化
- 实施方案只保留使用低代码平台2.0。将原本使用原生开发、低代码1.0的租户的数据迁移到低代码2.0平台上去。
- 只保留代开发应用一种对接方式。代码处理完毕后,调用企微的迁移完成接口,即可使自建应用升级为代开发应用。恰逢企微不允许服务商再使用自建应用的对接方式为企业客户提供服务。
- 只保留租户特有域名一种对接方式,但暂时支持使用统一域名。这块比较难处理,需要租户企业授权我们才能修改客户的企微应用配置,所以我们让渠道的同事尽量帮忙找客户进行授权,在上线那天进行修改。如果租户因为某些原因没修改的,则我们记录在客户联系表格,在服务续费时再让其重新授权。一年后不续费的租户,也就说明了不再需要维护了。
# 实施流程优化的几个原则
- 缩短实施流程
- 减少人工操作的步骤
- 人工操作与外部对接的接口,即时反馈结果;尽量显示详细的错误信息、自行修复的方法
# 任务拆分、安排:
将目标细化为设计相对独立、独立后基本可用的小模块基本任务。
按照任务的紧急程度、重要性排序。
经验上,下游功能较多的功能模块重要性较高,可以近似认为是下游服务的重要性和紧急程度之和。
# 后端技术债问题:
# 过度设计
- 架构上的过度设计。使用了其他部门开发的,不成熟的自研出入网关,导致偶发调用异常、消息丢失。重构抛弃了这个自研的出入网关,换成了使用spring-cloud-gateway,由于企微的调用不会重试,所以企微过来的调用不限流。
- 代码上的过度设计。滥用了比如访问者模式之类的设计模式以及AOP,导致代码不容易理解、逻辑连续性出现断层。
这里访问者模式滥用,实际上是想迭代企微接口返回的列表数据,还有一个问题是没隐藏游标的遍历代码。这块我们用迭代器模式重构了。
AOP编写的业务代码重构为显式调用,通用的与业务无强相关的逻辑则保留使用AOP。
# 横向扩展性缺失
该产品很多场景需要从企微同步大量数据到本地,其中有员工、客户、聊天记录等文本数据;音视频、文件等资源。
- 使用消息队列、RPC等方式将任务分发到各个节点。
- 使用线程池,将任务分发到各个线程处理。
任务是否需要分发,分发到其他线程还是各个节点,主要看任务预估的执行时间。
- 任务完成时间非常快、数量少,则无需分发,工作线程循环处理。如:员工数据。
- 任务完成时间预估比较久、数量多、租户级别的,则考虑分发到各个节点。如:会话存档、客户交接、员工权限等定时触发的任务;以及会话存档文件的拉取。
- 在这之间的,则分发到各个线程处理。如:拉取企业的客户数据。
也可以把多个任务,比如1000条任务,打包起来,再进行分发。毕竟线程切换、网络调用也是需要消耗资源的。
# 编码问题
编码问题比较多,这里先介绍一下trace传递失效问题。后续再写一到两篇博客详细介绍吧。
- 在service层的类的成员变量new了个线程池,导致提交任务时,没办法把上下文中的trace、租户id等数据带到子线程中。如果使用的是sleuth,只需要把线程池注册到spring,sleuth就会包装线程池,使其可以传递trace。
- 直接使用http client创建request,但没把trace设置到请求头。重构成使用feign+okhttp作为客户端了,sleuth天生支持feign的trace传递。
# 接口适配
本质上是缺少了接口接入防腐。
需要引入一层接口防腐层,用于调用远程接口,并且做一些对接逻辑的处理以及数据映射。简洁架构、洋葱架构、六边形架构、DDD相关的架构风格都有提及。实际上,在本次重构中,把项目的代码整理成了六边形架构,把零散的代码整理成统一的架构风格。
原先的代码是直接在业务代码里使用http client调用的,还要用json object处理数据,导致业务逻辑和调用逻辑耦合在一起,改起来非常浪费时间。后面我们引入了一层防腐层,里面还处理了企微的多个secret以及对应的token,用了feign+okhttp作为客户端,要一个小伙伴花了2天时间才改完。