摘要:在日常业务的研发过程中,由于需求的紧迫性,往往留给研发团队的时间相对有限。这种情况下,为了确保业务尽快上线,不得不在一定程度上牺牲设计的严谨性和技术的前瞻性,虽然短期内看似解决了燃眉之急,但长此以往,却逐渐累积了大量的技术债务。
背景
在日常业务的研发过程中,由于需求的紧迫性,往往留给研发团队的时间相对有限。这种情况下,为了确保业务尽快上线,不得不在一定程度上牺牲设计的严谨性和技术的前瞻性,虽然短期内看似解决了燃眉之急,但长此以往,却逐渐累积了大量的技术债务。
技术债,又称设计债或代码债,是指在软件开发过程中为了追求快速交付而采取的非最佳实现方式所带来的后续成本。这种“债务”可能源于使用了临时性的解决方案、忽略了代码质量、或没有遵循良好的设计原则和实践。
随着技术复杂度的逐步提升,系统的可维护性不可避免地受到侵蚀。这种演变并非线性的递增,而是呈现出指数级的增长趋势,最终会导致需求吞吐、交付质量双双下降。
为进一步提升服务品质和用户体验,斗拱进件板块决定启动客户全旅程项目,实施客户体验的深度改造。但是在整个项目的研发实施过程中,工程师们发现由于业务逻辑与系统模块的复杂与紧耦合,在开发的时候碰到了较多的困难与挑战,包括研发代码的周期与测试的质量等等,虽然最终有惊无险,顺利完成交付,但是我们发现,进件系统的维护与扩展已进入一个新的难度等级,必须启动战略性重构升级计划,为业务可持续发展夯实基础。
问题分析
经过多年的持续迭代投入,斗拱的整套进件体系功能越来越强大,代码量也越来越多,一次完整的进件,要由大量的微服务共同协作完成。从应用架构层面,进件系统可分为:以领域层为复用核心,以功能层为交付重心,以交互层为体验迭代中心,这样的一套三层架构体系:
交互层:包含API、WEB页、APP等交互逻辑与服务;
功能层:组合领域层原子接口,形成业务功能向交互层输出;
领域层:提供抽象过后的原子服务能力,场景关联性弱;
该分层架构在人员配备充足,业务及团队稳定的情况下,是能够充分发挥其设计效能的。然而,随着斗拱业务规模的持续扩大,面对多产品线高并发迭代的工程挑战与客户需求的爆发式增长态势,研发资源的配置根本满足不了业务的吞吐速率要求。为确保重要业务目标的顺利交付,部分团队不得不采用敏捷交付机制,在架构设计层面进行必要的动态平衡——通过建立架构治理框架下的技术决策机制,允许在关键路径上实施阶段性灵活调整,同时为体系化重构预留技术窗口。
其次,在推进支付平台化建设的战略进程中,由于缺乏成熟的范式可供参考,我们特地组建了专门的技术委员会。面对各行各业的多样化需求、各类用户角色以及纷繁复杂的场景故事,如何在一套平台上实现这些功能,成为了委员会频繁讨论的焦点:是沉淀复用还是个性化定制?是仅考虑某一端还是全面支持所有端?是做到交互层、功能层还是领域层?各方观点不一,但都有其独到之处。
长期运作下来,进件系统各层级,都在大规模的并行迭代,以致于:
第一,用户交互层与功能层之间,出现了比较多的冗余逻辑,加剧了系统的复杂度。
第二,功能层与领域层之间,也存在相似的重复代码现象,技术债务累积。
再者,功能层主要由管理服务和查询服务两个团队负责。其中,管理服务涵盖了客户体验的方方面面,包括但不限于进件材料与流程、业务配置与开通、协议签署,以及一系列配套的运营支撑功能。这些功能不仅要满足标准化的业务需求,还需兼顾用户的功能体验和重点客户的个性化定制。随着时间的推移,管理服务逐渐变得愈加庞大复杂,后续的维护与扩展难度也随之逐步增加。
最终,管理服务成为了交付瓶颈,需求越积越多。当新增客户全旅程这样一个非常大的业务能力时,我们来到了复杂度指数上升的边沿。
重构思路
重构(Refactoring),即还技术债,是指在不改变软件外部行为的前提下,对软件内部结构进行优化和调整的过程。其主要目的是提高代码的可读性、可维护性和可扩展性,从而降低后续开发和维护的成本,提升交付质量和效率。
论及重构,领域驱动设计(DDD)无疑是最先映入脑海的方法论。没错,我们也正是借助DDD的战略设计,对子域进行了重新划分,形成商户、审核、协议等子域,通过DDD的战术设计,将一些共通的逻辑提炼出来,形成业务权限、计费等模块。
领域驱动设计(Domain-Driven Design,简称DDD)是一种软件设计方法论,它强调以业务领域为核心进行软件设计,通过创建丰富的领域模型来反映业务逻辑,从而实现业务和技术的统一。
整个重构落地也是非常复杂,为了确保业务连续性不受影响,我们通过系统化、工程化的手段,分批次启动项目实施。整体分为两大阶段:
阶段一:领域拆分与沉淀
1、原斗拱进件板块,按照DDD的设计思想,重新进行领域的划分;
2、按照新的划分(子域A、子域B...),将交互层、功能层的逻辑沉淀到各个领域;
3、扩充领域职能,使其不仅提供内部服务,同时提供对外的API及应用组件;
核心思路:把原先按功能职责拆分的横向切分架构,重构为按领域职责拆分的纵向切分架构,如下图所示:
阶段二:领域模块升级优化
1、在每个领域内,针对较难维护的领域模块,进行二次重构、升级、优化;
核心思路:各个领域团队内部不定期发起重构(可能大可能小),升级优化自身负责的系统代码,提升其可维护性及可扩展性。
本篇文章中,我们主要探讨阶段一,后续的重构文章系列中,我们再逐步探讨阶段二的技术故事。
领域拆分与沉淀
1、领域划分
在启动重构之前,至关重要的是要进行广泛而深入的沟通与交流,以明确各个领域的定义、内容及其边界。这一过程中,我们应用DDD方法论,围绕用户故事对上层服务的业务逻辑,尤其是功能服务进行全面梳理。此环节需大量业务专家及系统负责人的积极参与,经过多轮次的讨论与碰撞,最终达成共识,并明确各领域的负责人。
接下来,我们将斗拱进件的对外API接口清单、控台页面菜单、内部功能服务接口清单逐一列出,并根据其所属领域进行归类。对于那些同时涉及多个领域的页面、内外接口,就由熟悉相关业务的领导根据其经验作出决策,给到最合适的领域。
一旦确定了领域归属,接下来的任务便是从代码层面着手,对所有的API服务、页面及BFF(Backend For Frontend)服务、功能服务进行改造与迁移工作,将其合理地拆分并沉淀到各自对应的领域服务中。
2、代码实施
整个代码实施过程,分为五步:着色->代码拆分->服务拆分>数据库拆分->整理&优化。
步骤一:着色
对后端BFF服务、功能层服务中,所有Public的API方法、Service方法、Repository方法等,添加自定义@Domain注解,以标识其所属领域。对于被多个领域同时使用的公共方法,要么标记多个领域名称,要么标记为All,即所有领域。
说明:此过程同样涉及大量讨论与交流。为了最终达成共识,建议按固定一个人的拆分思路统一开展,出现争议由其来拍板,以免陷入无休止的争辩而无法说服彼此的局面。
步骤二:代码拆分
在不改变服务化结构以及微服务调用入口的前提下,将@Domain标记的代码及其递归引用的私有代码,全部复制到为各个领域新建的独立工程目录中,此时需修改方法提供方及方法调用方相应的import包路径,标记多个领域的方法就复制多份,标记为All的公共方法,就每个域都复制一份。
说明:这个过程比较复杂的地方是依赖识别,我们是在Idea插件的基础上自主研发工具实现的。迁移完成后,新版本在生产环境中运行至少两周,以验证其稳定性和可靠性。
步骤三:服务拆分
在调用入口不变的前提下,将跨领域的内存调用代码,转成微服务远程调用代码。此时,微服务的数量会显著增加,大致等于被拆分微服务个数乘以领域数量,此时每个服务就可以独立迭代需求了。同样,该过程也是通过工具完成,微服务调用代码是按统一模版格式生成的,所以类及方法的命名会存在一些不人性化的情况。
说明:这个过程会遇到比较多的问题,如:内存全局变量问题、数据库事务问题、抽象继承类问题、异步通知问题、超时时长设置问题等,我们在后续的重构系列文章中再行展开。迁移完成后,新版本在生产环境中至少稳定运行两周,代表该阶段的完成。
步骤四:数据库拆分
为各领域创建独立的数据库,并将各领域独有的库表及数据内容迁移至新库中。针对那些跨多个领域的共享表,则要进行数据拆分与迁移,该一步骤相比前面的代码拆分、服务拆分,工作量和复杂度都有显著上升。鉴于该步骤短期内难以完成,同时考虑到以下实际情况,我们决定暂时搁置,未来择机实施:
■ 对所有数据库表,进行领域归属划分,每张表仅归属一个域,针对该表的结构及关键数据变更,均由所属领域团队完成,避免引入跨领域协作问题;
■ 在服务拆分阶段时,大部分跨领域的数据操作都已转成微服务调用,只有少数因性能、事务等考量的查询,直接通过Mapper跨库访问。
■ 跨域共享表的数量不多,由其他域代为维护的规模不大,复杂度尚在可控范围内;
步骤五:整理&优化
拆到各个领域的微服务,整体是包含了原后端BFF服务、功能服务全部的源代码,工程包非常大,类也非常多。各个团队需要手动整理这些代码,剔除不属于本领域的部分以及未被使用到的函数和方法。此外,对于那些在服务拆分阶段产生的工具型微服务接口,我们使用@Deprecated注解予以标记,明确表示这些接口不再接受迭代更新,若未来有需求要迭代,则按照标准的接口规范重新设计实施,以此逐步淘汰工具型的接口。
3、重构效果
在历时三个多月的项目实施过程中,我们遇到各种各样的挑战,也逐一克服。整个项目的复杂度极高,参与进来的都是各团队的核心成员及众多资深架构师。最终,项目也取得了令人瞩目的成果。
从业务角度统计,进件相关需求的交付吞吐率提升了30%,缺陷密度(上线100个需求引入的缺陷数)下降30%。
从技术层面来看,日常需求的协作模式从原先前端、功能和领域的多方协作,演变为单个领域团队为主;所有原领域层研发人员全面参与到业务开发中,显著提升了他们的业务经验;所有技术TL积极参与了领域边界的讨论,对各自边界有统一清晰的认识,很少再出现边界争论;原功能层的管理服务,在经过重点拆分和沉淀后,系统的维护复杂度明显降低。
总结
斗拱进件体系的重构实践,充分彰显了直面技术债务的重要性。通过缜密规划与严谨执行,我们不仅实现了重构的目标,还显著提升了业务和技术效能。这不仅是对技术架构的一次优化,更是团队协作能力与技术实力的全面提升。
来源:陆家嘴金融网