做财务软件的朋友都懂,核心代码动一下简直是牵一发而动全身。
改一个小小的功能点,原来跑的好好的报税模块突然报错,对账逻辑出问题,最后要花好几天回溯问题找bug,搞不好还要上线事故。
老板催着要给大客户加个专属的发票识别功能,现有框架塞进去,整个核心版本都变得臃肿,普通用户用不上还抱怨软件越来越卡。
你不想动核心,又想快速出功能,插件架构就是解决这个问题的标准答案。
我之前待的团队做中小企财务软件,最早就是所有功能堆在核心包里,每次版本更新,测试都要把所有核心流程跑一遍,光是回归测试就要花一周。后来接了一个连锁客户的定制需求,要加多门店自动核算,当时硬着头皮往核心塞,上线之后连续三周天天熬夜改bug,那滋味真的不想再来第二次。
后来我们沉下心重构插件架构,做完之后才发现,原来加功能可以这么轻松。
很多人一开始就走错了方向,以为插件就是把代码拎出来放另一个工程,其实根本不是这么回事。
核心层必须是稳定的,只放所有场景都离不开的东西:比如账务的核心记账逻辑,科目体系管理,基础的权限校验,还有给插件用的扩展接口规范。你说什么功能放核心?一句话,去掉它整个软件就跑不起来的,才放进去。
剩下所有的个性化功能,不管是通用的发票OCR,还是大客户的定制化核算,全塞插件里。插件不能改核心的任何代码,只能通过核心留好的接口调用能力。
对,就是这么简单的规则,但是能解决80%的功能冲突问题。
举个例子,我们现在做发票导入功能,做成一个独立插件,核心根本不知道这个插件存在,插件只调用核心预留的“创建凭证”接口,把识别完的发票数据生成凭证存进去就行。哪怕发票识别的逻辑改一万次,核心代码碰都不用碰,更不可能影响报税、对账这些核心流程。
你会不会问,插件要调用核心的数据怎么办?不会让插件直接连核心数据库吧?当然不行,直接连库那核心的规则一下子就破了,以后数据乱了都找不到是谁改的。
核心要给插件提供统一的数据查询服务和操作接口,插件只能通过接口拿数据,改数据必须走核心的校验规则。相当于核心给插件开了一扇门,你只能走这扇门进来,不能翻墙。
接口怎么设计才对?很多团队设计接口的时候,想着以后有什么需求就加什么接口,最后接口乱成一锅粥。
不对,要按扩展场景来分类。
比如财务软件里,插件最常改的就是凭证生成的逻辑,那核心就留一个凭证生成的扩展点,所有要生成凭证的插件都实现这个扩展点就行。再比如,月末结账的时候,有些插件要加自己的结账校验,那核心就留一个结账前置的扩展点,插件把自己的校验逻辑注进去,结账的时候核心会自动把所有注册的校验都跑一遍。
这种就是“事件+监听”的思路,核心只负责发事件,插件监听事件做自己的处理,核心完全不用管有多少插件在监听,谁监听了。
还有一种方式,就是插件自己注册菜单和页面,核心只负责把插件注册的页面渲染出来,完全不用管页面里做什么。比如你加一个库存核算插件,自己在侧边栏加一个库存菜单,点进去全是插件自己的页面,核心只做路由跳转和权限校验,其他一概不管。
我们试过最舒服的,就是把插件的生命周期都交给核心管理,插件启动的时候向核心注册自己,停用的时候就把自己的资源都释放掉。你要加新功能,直接后台装插件就行,不用重启主程序,不用更新核心包,用户在线就能更。上个月给一个客户加定制的出口退税申报,我们在线直接装插件,客户那边根本没感觉到重启,核心功能一点都没停,太爽了。
你有没有遇到过两个插件改同一个数据,最后出问题找不到是谁干的?那就是隔离没做好。
每个插件都要有自己独立的数据表空间,插件自己的业务数据存在自己的表,不能和核心混,也不能和其他插件混。核心只存和核心有关的数据,插件的数据自己管,哪怕插件删了,核心数据一点不受影响,用户只要删插件就行,不用改核心数据库。
还有类隔离哦,很多人用Java开发,所有插件共用同一个类加载器,两个插件用了同一个第三方包不同版本,直接就冲突了,启动就报错。这个坑我们真踩过,之前两个插件一个用了低版本的POI处理excel,一个用了高版本,最后两个插件都用不了,搞了一天才找到问题。
后来我们用了独立类加载,每个插件加载自己的依赖,互不干扰,这个问题直接就解决了。哪怕你用Python或者JS开发,也可以做隔离,比如用虚拟环境或者模块化隔离,把每个插件的依赖分开,别混在一起就好。
还有权限隔离,插件只能拿核心给它的权限,不能越权访问核心的其他功能,也不能访问别的插件的数据。比如一个发票插件,只能访问和发票相关的数据,不能去碰用户的核心报税数据,安全上也更稳。
说几个我们踩过的坑吧,别再走我们的老路了。
第一个坑,不要一开始就追求完美的插件架构,你刚做软件的时候,功能没几个,上来就堆插件架构,反而把简单问题复杂化。你先把核心跑起来,等扩展需求多了,再慢慢拆插件不迟。
第二个坑,不要让插件依赖太多核心内部逻辑,核心变一点,所有插件都要改,那你做插件架构还有什么意义?核心的接口一旦定好,就不要随便改,要改也要向下兼容,老插件还能跑。
第三个坑,插件也要能独立测试,你做插件的时候不要依赖核心一起启动才能测,核心给插件提供测试模拟接口,插件自己就能跑测试,改完自己测就行,不用每次都拉上核心整个测一遍,测试效率能提高好多。
最重要的一点,绝对不能让插件修改核心的全局状态。有插件改了核心的某个全局变量,结果另一个插件读了错的数据,出了问题你查三天都查不出来。规矩定死,只能读,不能改核心的全局状态,有什么需求走扩展点,绝对不能破这个例。
最后其实受益的不只是开发,还有用户和产品。
产品要快速试错,新功能先做成插件,推给部分用户用,用得好再留下来,不好直接删掉插件,核心根本不受影响,不用等到下个大版本更。
用户想要个性化功能,不用等我们发版,我们给客户定制完插件,直接给客户装上就行,不影响其他用户的使用,也不用把定制功能塞到核心包,让所有人跟着变卡。
开发呢,不用天天担心改核心出问题,每天加班改bug,加新功能只需要改自己插件的代码,心里踏实多了。
原来我们发一次版,整个团队都要紧张好几天,现在发版只更核心或者更插件,回归测试范围一清二楚,上线稳得很。
其实说白了,插件架构的核心就是让稳定的更稳定,让变化的更灵活,核心不动,功能随便加,这不就是我们做软件开发一直追求的吗?如果你也在做需要不断扩展功能的系统,尤其是像财务这种核心逻辑不能乱的软件,真的可以试试这套思路。

