如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦!
1.复杂度综述
-
什么是复杂度
- 软件设计的核心在于降低复杂性。——《软件设计的哲学》
- 斯坦福教授John Ousterhout从认知负担和工作量方面给出了一个复杂度量公式:
- 公式
- 子模块的复杂度
cp
乘以该模块对应的开发时间权重值tp
,累加后得到系统的整体复杂度C
。 - 如果一个子系统特别复杂,但很少使用及修改,也不会对整体复杂度造成太大影响。例如,Spring框架内部代码较为复杂,但由于几乎不需要我们去变动,所以对系统的整体复杂度影响并不大。
- 公式
-
复杂度分类
本文主要面向业务复杂度的治理。
- 业务复杂度高的影响
- 研发成本高:需要花费更多的时间去理解、维护代码;同样的需求,可能需要修改更多的工程和类。
- 稳定性差:过高的业务复杂度会导致系统难以理解甚至理解出现错漏,改动代码后极易出现“按下葫芦起了瓢”的问题。
2.业务系统复杂度高的常见原因
-
业务系统模块多,关系复杂,互相依赖
- 例如电商业务,包含商品、订单、采购、库存、财务等多个系统,系统之间有各种各样的依赖关系。如订单系统依赖库存充足才可以正常下单,采购依赖商品必须创建才可以发起采购;而系统内部又可以划分为多个子系统,如订单系统可以包含接单、营销、会员等各个子系统。
-
代码晦涩,从代码中很难找到关键信息
- 例如以下业务处理代码,不点进具体的方法,根本不知道做了什么:
/** * 处理业务逻辑 */ public void handleBiz(){ step1(); step2(); step3(); }
- 再比如以下业务处理代码,做了方法定义外的操作,开发者很容易遗漏重要信息:
/** * 转换对象方法 */ public Po convert(Dto dto){ Po po = new Po(); po.field = dto.filed; // 更新操作,不应该放到转换方法里 mapper.update(po); // 调用RPC服务,不应该放到转换方法里 XXGateway.update(dto); return po; }
- 例如以下业务处理代码,不点进具体的方法,根本不知道做了什么:
-
业务规则、流程变化多,变化频繁
- 大量的变化造成花在系统上的时间增多,提升了开发时间权重;
- 繁杂的业务规则写在代码中,难以理解、梳理。
3.降低业务复杂度的方法
- 抽象分治,分解复杂度
- 领域拆分:将与核心概念有关的内容抽取/合并,形成独立的领域。
- 实体类的系统:映射到物理实体,比如商品中心、用户中心、地址服务等。
- 流程类系统:映射到多个角色的串联协调工作,比如供应链上单,审核类系统等。
- 计算任务类系统:映射到虚拟计算机类及数据处理,比如搜索排序、推荐计算等。
- 领域拆分:将与核心概念有关的内容抽取/合并,形成独立的领域。
供应链领域化拆分例子:
- 领域之内拆分
- 变与不变拆分:将不易变化的系统能力拆分出来,上层适配各种业务逻辑,底层提供稳定的能力单元。如营销领域,将优惠卡券这个不易变的内容作为一个子系统来设计。
- 场景隔离:如B/C隔离,B端业务复杂度较高,流量较小,更注重数据建模、可配置、可扩展;C端业务复杂度低,但流量较大,更注重高性能、高可用。
- 示例:营销领域进行【变与不变拆分】+【场景BC隔离】
- 背景:营销域即有面向商家的营销资质、营销活动管理,又有面向C端用户根据活动领红包、优惠券的高频业务。
- 治理方案:建设营销B端服务,提供面向商家的营销活动管理,重心放在建设复杂的营销活动模型、处理复杂的业务流程;建设C端用户系统,提供面向C端客户的领营销资产、消费影响资产服务,重心放在高并发、高可用建设;建设底层营销资产管理服务,如卡券中心提供生成卡券、消费卡券、查询卡券等基础稳定的服务。相对不易变化,不需要经常迭代;上层的服务根据不同的业务场景来决定把卡券发给谁、什么时候发、发多少,业务场景变化或者有新的场景时,经常需要迭代。
- 拆分架构如下图:
-
添加注释,使代码易懂
-
代码思路要通过注释/自注释标识出来
- 例:Redis模式的库存操作,在处理逻辑主方法中,较为清晰地标注了每一步核心逻辑。
- 代码示例
-
注释应当能提供代码之外额外的信息
- 重视What和Why,而不只是代码是如何实现的(How)。
- 一些不那么直观的代码,可以附上原因。
- 设计思路,特别复杂的,可以考虑贴上设计方案的地址。
-
-
配置化
- 业务对象可配置
- 业务中用到的同类型对象特别多,使用硬编码方式维护困难时,可以考虑抽象出可配置化的对象配置中心。
- 例如:商品中心,将商品抽象为SKU,并提供名称、价格、重量等可配置的属性。
- 业务规则可配置
- 业务中规则部分特别复杂,可以考虑抽象出可配置化的规则配置中心。
- 例如:售卖策略配置,某SKU在某业务线+某业务场景中,必须搭配某种前置SKU,以XX价格进行售卖。
- 业界有多种开源规则引擎,如Aviator、Drools、QLExpress等,不同的规则引擎在功能、性能、学习维护成本上有一定差异,需要根据自己的业务场景来进行选型。
- 业务流程可配置
- 如果不同的业务场景,需要不同的执行流程,可以考虑引入流程配置框架。
- 例如:一些业务场景,流程执行顺序为A–>B–>C,另外一些业务场景执行顺序为C–>B–>A,还有一些业务场景执行顺序为B–>A–>C。
- 注:流程配置框架相对比较复杂,更适合平台/中台建设,其他场景建议谨慎评估后再引入。
- 流程配置框架的核心:流程编排+能力复用(插件化),前提是流程抽象➕流程标准化。
- 业界流程编排框架:阿里TMF、美团BPF、京东物流batrix。
- TMF2.0 配置流程:
- 业务对象可配置
-
使用规范降低复杂度
- 本质上,是做好约定,简化思考。如:一个类命名为OrderDao,不需要看代码,就可以很清晰地知道这是一个订单数据处理的类。
- 代码规范:如接口、类、方法等的命名。
- 架构层级规范:系统分层级。上层调用底层,避免底层直接调用上层,同层级尽量避免互相调用。
- 例如:物流百川三层架构,定义了系统层级,使系统交互有序。
- 架构图
4.业务复杂度优化重构原则
- 小步快跑:每个迭代要能独立交付,保障每次迭代充分验证,更快看到重构效果。
- 先写后读:通过双写,验证新模型的可行性;通过数据一致性校验后,再逐步迁移读接口。
- 先轻后重:先做简单逻辑再做复杂逻辑。先迁移轻业务,有了经验后,再去迁移更复杂的重业务。
希望这些经验和方法能够帮助你在实际工作中更好地管理和降低业务复杂度!
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们处理,核实后本网站将在24小时内删除侵权内容。