作为一个后端研发,我是如何进行系统重构的

原文发表于我的博客coders.run/how-did-i-d...

在上一篇文章里面,我介绍了作为一个后端研发,我是如何做业务抽象的。但是在日常工作中,业务往往是不断演进、不断变化的。这也就意味着系统并非一开始就是我们当前看到的样子,而是经过了长期的演化迭代后,才成为了当前的形态。那么这里便存在一个矛盾------如果我们无法提前预判到业务和系统在未来较长时间范围内的形态,在技术上我们如何保证当前的抽象是在较长时间范围内是合理的?这就需要在合适的时候对系统进行重构,从而保证业务抽象的合理性了。以下我将以最近的一次系统重构经历,来介绍作为一个后端研发,我是如何进行系统重构的。

是时候进行系统重构了

去年中旬由于种种原因,我们从其他的研发团队接手了一套类工单系统。经过这几个月的需求迭代,我们越发感觉到这套系统在原有的技术实现下难以快速支撑新的业务诉求,新需求研发成本巨大。拆开来看,造成研发成本高的原因有以下几点:

  1. 组件复用程度不够,导致研发理解成本高: 驱动工单流转的流程编排组件是公司常用基础组件之一,公司内部已经有了一套通用的、可视化的、可配置的流程编排组件。但是在系统实现过程中,原研发团队完全自己手搓了一套流程编排模块,而不是复用公司的通用组件。该流程编排模块没有任何可视化能力,需要将一个巨大的配置文件和模块代码对比解读查看,才能看清不同来源的工单到底经过了哪些流程节点、在每个流程节点做了哪些处理。
  2. 模块划分不合理,边界模糊: 流程编排模块理论上应该是一个底层基础模块,其自身运行过程中流程相关逻辑和数据应该与工单的业务逻辑和数据相互分离。而原研发团队在处理时没有做清晰的模块边界划分,逻辑和数据相互耦合,例如:页面上显示工单内容时,后端需要将工单数据库表和流程数据库表的数据经过复杂逻辑拼接,才能向业务同学显示完整的工单内容。
  3. 相似功能未做抽象建模,定制化重复实现:工单在不同处置阶段需要派单给不同的业务团队,但是在派单优先级、人员可派单上限控制、超时回收、消息通知等各方面存在差异,而在系统原有的实现中针对不同的处置环节存在多套派单能力。

这些问题导致了新需求方案设计时必须耗费大量时间评估其影响,大大拉长了研发周期,并且在问题排查时难以快速定位到原因。虽然在前期阶段系统的设计实现方案可能是合理的,但随着业务的发展和系统的迭代,原有的业务抽象已经不再适应新的场景。因此,是时候进行系统重构了🔨🔨🔨🔨🔨🔨

找到通用业务流程

我们分析了系统原有实现中的问题,并与业务团队和产品团队多轮沟通,讨论了未来一年业务的发展方向和目标,然后技术团队对系统进行了大刀阔斧的重新设计和重构。我们将工单生命周期拆分为了『数据接入』、『工单聚合生成』、『工单研判』和『工单处置』的4大通用业务主流程节点。在『工单研判』和『工单处置』节点中,又包含了诸如『研判/处置跳过判定』、『研判/处置任务初始化』等子流程节点。

基于通用业务流程的技术模块划分

继续上述的业务流程,我们将业务逻辑抽象出如下的技术模块:

  1. 数据接入模块: 数据接入模块负责与上游系统交互,接收上游系统推送的数据,进行准入判定、字段剪裁、格式标准化等工作
  2. 工单聚合生成模块:不同上游系统推送过来的数据可能描述的是同一件事情,为了提升业务的处理效率,工单聚合模块结合算法能力,将上游多条原始数据聚合成一条工单
  3. 原子方法管理器 :上述子流程中的各节点均对应系统中的不同原子方法。每个原子方法入参只有工单id,出参只有ture/false(表示原子方法是否执行成功)和一些简单的结果信息。这些原子方法注册到原子方法管理器,由原子方法管理器统一维护和管理
  4. 流程编排模块: 真正的流程配置在公司通用的流程编排组件上,系统中的流程编排模块只起到系统逻辑与公司通用流程编排组件间的中转路由作用
  5. 派单模块: 派单逻辑统一到派单模块,该模块对外提供有限的接口给到派单原子方法调用。派单模块内部通过不同优先级的派单池、派单上限控制、超时回收等可配置化的手段保障正常派单
  6. 消息推送模块: 将所有的消息推送统一抽象为"当某件事情发生时,如果满足某种条件,则向某些人/群发送某些内容的消息卡片"模式。比如: 工单创建时,如果工单数据来源包含A,则向业务同学B发送包含工单名称、工单来源、工单创建时间的消息卡片;工单处置结果提交时,如果处置定级为『严重』且工单数据来源包含A,则向业务群C推送包含工单id、工单名称、工单数据来源、工单处置人、工单处置定级等内容的消息卡片

上述6大模块相互独立,各司其职,各个模块无需了解其他其他模块内部逻辑,做到了模块之间的互相解耦。

通过模块的组合来支撑上层业务

那么,如何通过上述技术模块来支撑上层业务呢?让我们来模拟一个完整的业务流程:

  1. 上游系统报送的数据先进入数据接入模块,数据接入模块对数据处理后,通过MQ发出数据写入事件
  2. 工单聚合模块监听到MQ消息,基于规则结合算法能力确定该条数据是聚合到一个存量工单上,还是创建一个新工单。工单聚合模块内部调用流程编排接口。
  3. 流程编排模块内部确定需要走哪个子流程,从而调用公司通用流程编排组件(研发已事先在该组件后台配置好了的各个子流程)
  4. 公司通用流程编排组件会回调系统中注册在原子方法管理器中的各个原子方法(每个原子方法对应组件后台配置流程中的一个个节点)
  5. 当回调到研判/处置派单原子方法时,原子方法内部会调用派单模块。
  6. 派单模块内部计算出派单人后,会通过MQ发出派单事件。事件被消息推送模块监听,从而执行规则判定、消息内容构建并进行飞书消息推送。
  7. 派单人收到飞书消息后,进行工单任务认领。
  8. 派单人提交了任务处置结果后,结果数据先落任务表。同时系统会调用流程编排模块,驱动下一个流程节点的执行。

系统各个模块的组合关系如下图所示:

总结

通过本次系统重构,我们抽象了6大技术模块,通过它们的组合实现了上层业务的差异化流程。新架构大大降低了系统复杂度,提升了研发效率和系统稳定性。同时,流程链路上的一些逻辑支持配置化,为后续支持业务自主化接入打下了基础。


好了,本次分享介绍,如果大家觉得对自己有帮助的话,欢迎点赞/收藏/分享 。如果你想了解更多关于系统重构、技术架构以及提升研发效率的内容时,也十分欢迎访问我的博客 coders.run。在这里,你将发现更多有趣的技术文章、实用的开发经验分享以及行业动态分析。让我们一起探索技术的无限可能,共同进步成长!

相关推荐
rolt4 天前
[pdf,epub]162页《分析模式》漫谈合集01-35提供下载
ddd·架构师·uml·领域驱动设计·面向对象
老肖相当外语大佬4 天前
DDD之理解复杂度、尊重复杂度、掌控复杂度
ddd·领域驱动设计·复杂度·软件成本
老肖相当外语大佬10 天前
懂了这个道理,人月神话不再是神话!
ddd·领域驱动设计·人月神话·交付效率
小码编匠20 天前
领域驱动设计(DDD)要点及C#示例
后端·c#·领域驱动设计
老肖相当外语大佬22 天前
解决DDD最大难题-如何划分领域
ddd·领域驱动设计·软件设计
rolt1 个月前
[pdf,epub]105页《分析模式》漫谈合集01
ddd·架构师·uml·领域驱动设计·分析模式
rolt1 个月前
[答疑]是不是互联网更适合用DDD
ddd·领域驱动设计
rolt1 个月前
有向无环图的约束怎么表达-《分析模式》漫谈39
ddd·uml·领域驱动设计·面向对象
老肖相当外语大佬2 个月前
反DDD模式之“复用”
开源·实战·ddd·领域驱动设计
xin4972 个月前
领域模型和数据模型还傻傻分不清? 如何实现领域模型
后端·领域驱动设计