关于模块化开发的思考

缘起为曾经的一次面试,先介绍下背景。还是以本人现在工作中的业务背景为例吧,二级市场的业务有三种模式:转让、回购、质押,这三种模式构成了二级市场这条业务线的全部功能,这三个功能是分别互斥的,这导致三个功能之间彼此会有些依赖,比如回购时需要看下当前资产是否发生了转让或者质押。当前我们的业务量还不是太大,在搭建应用时,这三个功能都在同一个app中,考虑到未来万一哪天业务量大了,可以方便的(尽可能少改代码,且不用去更改核心业务代码)把这三个功能分别拆分,我们在包结构上尽可能做到清晰,将来迁移的时候直接拷贝几个包,改非常少的代码就可以了,如下图:

其中srv包分别包含transfer,repurchase,pledge三个功能模块的具体实现,client包中主要是提供给其他功能访问的client,比如transfer提供一个client给repurchase访问(srv.repurchase需要访问client.transfer来确定是否已经发起过转让(达到互斥的目的))。common包主要是一些大家共用的代码。未来如果业务量大了,比如repurchase这个功能要独立拆出来,那么我们只需要将srv.repurchase和common以及client.transfer,client.pledge整个包原封不动的拷贝到新的app中,修改下client.transfer中访问srv.transfer的方式(client.pledge同理),即可以完成应用拆分,改动量少,最重要的一点,核心功能的业务代码完全不用去改动(改核心功能的业务代码是一件非常有风险的事,尽管你可能只是改寥寥几行代码)。假如将来有一天突然要合并到一个app中也很方便。举个例子说明这个问题,首先,在srv.repurchase下有一个类RepurchaseService,在发起repurchase时需要调用transfer校验信息:

package com.secondary.market.srv.repurchase;

import com.secondary.market.client.transfer.TransferClient;

public class RepurchaseService {
    /**
     * 发起回购
     */
    public void applyRepurchase(){
        //自身规则校验
        //
        //校验是否已经发起过转让了
        if(TransferClient.transferApplied(new Object[1])){
            throw new BizException("transfer applied");
        }
        System.out.println("apply repurchase");
    }
}

这时,我们在client.transfer包下提供一个类TransferClient,通过它去访问srv.transfer下TransferService执行具体的判断逻辑:

package com.secondary.market.client.transfer;

import com.secondary.market.srv.transfer.TransferService;

public class TransferClient {
    public static boolean transferApplied(Object[] args) {
        //调用TransferService判断是否发起了转让
        return new TransferService().transferApplied(args);
    }
}
package com.secondary.market.srv.transfer;

public class TransferService {
    public boolean transferApplied(Object[] args){
        //业务逻辑处理
        return true;
    }
}

如果将来我们把repurchase这个功能需要拆分出去成为一个独立的app,大致是这样的:

上图是大致的包结构,核心代码RepurchaseService完全不用动,我们只需要改下TransferClient的实现,改为rpc调用即可,而无需跑到核心业务流程的包中去修改代码:

package com.secondary.market.client.transfer;

public class TransferClient {
    public static boolean transferApplied(Object[] args) {
        //这里改为rpc调用判断是否发起了转让,对于RepurchaseService的调用是无感知的
        return true;
    }
}

当然原来transfer功能就需要提供一个controller或者resource来接收这个rpc访问,本质上这里也没有改动到任何核心功能业务代码,可以理解为仅仅是对原来的TransferService做了下facade,套了层马甲。

总之我们的目标在于能够像一辆汽车一样,在有需要的时候能够方便的对其进行拆卸和组装,但目前至少这些功能模块还是在同一个应用中,背景大致是这样。

问:如何保证后面开发的同学遵守你这个规则呢?比如repurchase要判断当前资产是否已经transfer,正确的做法是srv.repurchase这个包中的某个类应该访问client.transfer中的某个类,但是某位童鞋直接调用srv.transfer中某个类,后面的童鞋发现有这个先例后也开始这么干,这样代码发展下去相互嵌套,功能之间成了网状关系,后面拆分起来十分不易。

答:每个重要功能上线前,我们都会组织一次codereview,通过cr可以保证开发同学遵守这个规则

问:这个需要人工线下约束,还有其他办法吗?

答:暂时没有想到其他办法,您有啥建议?

问:osgi了解过吗,可以用它进行模块化开发

osgi?这玩意儿我知道啊,很早就出现了这个东东了,貌似是有这个功能,但我只记得它支持热部署,面试后我赶紧去翻了翻osgi(equinox),没有深究其实现原理,主要看了下开发步骤等,总体感觉下来:

1,做到了接口与实现的分离,每个bundle只暴露自己特定的接口

2,osgi的模块化感觉是为了动态化而产生的,也就是说因为有了动态化(热插拔)的需求而产生这个东东,但是我们的动态化需求不是这么明显,不大用得上,或者说我们这样的“小需求”,用这么高端的东东,有点重了

3,挑战所有开发童鞋的开发习惯,目前team内没有人使用osgi方式进行开发,这增加了学习成本,同时team内没有一位非常精通osgi的老司机,使用osgi进行开发有未知的各种风险。

4,即使采用osgi,在我们的场景中,还是不能起到约束开发的作用。比如我们有基础服务productService(bean),未实现任何接口,transfer,repurchase,pledge这三个功能都会访问获取产品产品的相关信息,那么可能存在transfer中写一个productService,在其他包内也写了一份,最终还是得靠cr才能发现!

后来在网上逛的时候发现阿里开源了一款java模块化开发框架jarslink,去学习了下,总体感觉下来:

1,比osgi更加轻量级,学习/开发起来相对简单点

2,个人认为还是适用于有动态化的需求,官方也给了应用场景,但我们的需求用不上

后来又看到java9继承了模块化开发,赶紧学习下:

1,jdk原生支持,不用在引入第三方jar包

2,module-info.java中requires,exports都是以package为单位的,所以,提供给外部模块访问的类最好能够统一放到同一个包中(可以有子包)

3,对于上面我们的例子,如果使用java9模块化大致如下:

比如在repurchase模块中,module-info.java配置如下:

module secondary.market.repurchase {
    exports com.secondary.market.repurchase.external;//导出RepurchaseClient
    requires secondary.market.transfer;//导入transfer module,但只能访问TransferClient类
    requires secondary.market.pledge;//导入pledge module,但只能访问PledgeClient类
}

看到了吧,如果我们需要使用到另一个模块的client时,requires是哪个模块的parent package,只是子包中的external对我们可见,比如上图中repurchase导出了external包,引入了transfer和pledge两个module。但是问题来了,根据我们的业务场景,transfer module也需要引入repurchasemodule,这就造成了循环依赖,在这里是不被允许的!!!so,java9的模块化开发方式也不适合我们。

 

综上,采用我们一开始的方案是比较适合我们当前的“国情”的,既简单也容易操作,不需要引入第三方jar,也不用升级jdk(目前我们还在用jdk8),不足之处是要在team内宣导这个规范,同时配以cr的方式保证后面的童鞋遵循这种方式,需要开发的童鞋要有一定的自律性。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值