虽然我们使用SpringBoot实现微服务,但是在一个微服务中还是有可能塞入很多模块;同时从单体向微服务过程中,只有先将单体切分成模块以后,这些模块之间的依赖越来越少,这些模块才能逐个独立成微服务。
有没有一个有效手段在当前单体架构下发现模块之间的依赖关系?发现了模块的依赖关系,如果依赖关系很多那么我们进行模块重构,也许这两个模块应该合并成一个模块,或者重构成了单线依赖,这样两个模块的依赖达到理想程度,可以变成了两个微服务了。
https://github.com/olivergierke/moduliths 提供了这样一个测试和发现模块之间依赖耦合的有效工具。
假设我们在一个包com.abc下面有两个子包com.abc.a和com.abc.b。 那么这两个子包是不是两个独立模块?依赖关系如何?
首先我们要使用@Modulith在com.abc这个根包下一个入口类里面注解一下:
package com.abc;
@Modulith
public class Application {
}
这个@Modulith类似于SpringBoot的@SpringBootApplication,如果使用SpringBoot,可以一起注释:
@SpringBootApplication
@Modulith
public class ModulithsApplication {
public static void main(String[] args) {
第二步,com.abc下有两个子包,假设是com.abc.a和com.abc.b,这里我们先假设一个情况,假设com.abc.a包下面的MyComponentA依赖于com.abc.b包下面的MyComponentB,我们这个工具是否可以检测出这种依赖呢?
com.abc.a包下面的MyComponentA:
@Component
public class MyComponentA {
private final MyComponentB MyComponentB;
下面是com.abc.b包下面的MyComponentB:
@Component
public class MyComponentB {
第三步,我们这个工具是通过测试环节编写测试用例才能检测出这样的依赖,我们知道在正常情况下,当我们启动SpringBootAplication时,Spring会扫描@Component元注解,然后一股脑子打入Jar包,这里MyComponentA MyComponentB都标注了@Component,所以肯定会打包在一起,我们的工具特别之处在于,会只扫描com.abc.a子模块包或com.abc.b子模块包,不需要你任何配置,只要在测试类上标注一个注解:@ModuleTest
比如我们在com.abc.a下建立一个测试类:
@RunWith(SpringRunner.class)
@ModuleTest
public class ModuleATest {
@Autowired
ApplicationContext context;
@Test
public void bootstrapsModuleAOnly() {
//有ModuleTest注解情况下会出错
context.getBean(MyComponentA.class);
}
}
当运行上面这个测试时,SpringBoot会报错:
Parameter 0 of constructor in com.abc.a.MyComponentA required a bean of type 'com.abc.b.MyComponentB' that could not be found.
因为MyComponentA依赖于MyComponentB,而MyComponentB在com.abc.b这个子包(模块)下,而这个模块并没有被测试类加载,所以没有发现该模块下MyComponentB。
报这个错误说明两个模块之间有依赖,如果报错越多,说明依赖越多,这时候需要重构了,如果只有一处依赖,我们可以使用Spring的event机制来替代这种直接调用,也可以通过第三方服务注册器进行发现调用。
总结
模块之间依赖我们需要在设计上及时发现并重构它,这样才能保证上下文边界的清晰,狗狗为什么一出门就要撒尿,据说它也是在画边界一样。