为何ArchUnit洋葱架构测试因SPI方法参数为领域模型报错?
洋葱架构ArchUnit测试报错解决方案
问题背景
我有一个严格遵循整洁/洋葱/六边形架构的演示项目,配置的ArchUnit测试代码如下:
@AnalyzeClasses(packages ="fr.tristan.demoassurance", importOptions = {ImportOption.DoNotIncludeTests.class, ExcludeConfig.class}) public class ArchitectureTest { @ArchTest public static final ArchRule onionArchitectureRule = onionArchitecture() .domainModels("fr.tristan.demoassurance.domain.entity..") .domainModels("fr.tristan.demoassurance.domain.event..") .domainServices("fr.tristan.demoassurance.domain.api..") .applicationServices("fr.tristan.demoassurance.application.controller") .adapter("message", "fr.tristan.demoassurance.infrastructure.message..") .adapter("persistence-mongodb", "fr.tristan.demoassurance.infrastructure.repository.mongodb..") .adapter("persistence-mysql", "fr.tristan.demoassurance.infrastructure.repository.mysql..") ; }
运行测试后出现报错:
was violated (1 times): Method <fr.tristan.demoassurance.domain.spi.message.MessageQueueSpi.send(fr.tristan.demoassurance.domain.event.PoliceAssuranceEvent)> has parameter of type <fr.tristan.demoassurance.domain.event.PoliceAssuranceEvent> in (MessageQueueSpi.java:0)
对应的问题接口代码:
package fr.tristan.demoassurance.domain.spi.message; import fr.tristan.demoassurance.domain.event.PoliceAssuranceEvent; public interface MessageQueueSpi { void send(PoliceAssuranceEvent policeAssuranceCreatedEvent); }
我有两个困惑:
- 不清楚违反了哪条规则,也不明白为何SPI接口不能以领域模型作为参数。
- 同一父包
fr.tristan.demoassurance.domain.spi下的另一个接口,其save方法同样以领域模型PoliceAssurance作为参数,但ArchUnit并未报错:
package fr.tristan.demoassurance.domain.spi.repository; import java.util.List; import fr.tristan.demoassurance.domain.entity.PoliceAssurance; import fr.tristan.demoassurance.domain.exception.PoliceAssuranceException; public interface PoliceAssuranceRepositorySpi { List<PoliceAssurance> findAll(); PoliceAssurance save(PoliceAssurance policeAssurance) throws PoliceAssuranceException; PoliceAssurance findById(Integer id) throws PoliceAssuranceException; void deleteById(Integer id) throws PoliceAssuranceException; }
问题原因
ArchUnit的onionArchitecture()规则会根据配置的包路径划分洋葱架构层级,只有明确归类到对应层级的包,才会被允许遵循层级间的依赖规则。
你的问题核心在于:domain.spi包下的所有接口都未被配置到任何层级中,ArchUnit无法识别它们属于领域层的一部分。虽然PoliceAssuranceRepositorySpi暂时没触发报错,但这属于规则检查的遗漏,MessageQueueSpi刚好触发了非法依赖校验。
在洋葱架构中,领域SPI(端口)是领域层的核心组成部分,原本就允许直接依赖领域模型(entity、event等),但必须被归类到domainServices层级中,否则ArchUnit会将其视为未定义层级,进而触发依赖违规的判定。
解决方法
修改ArchUnit配置,将domain.spi包添加到domainServices的配置项中,让ArchUnit识别这部分接口属于领域服务层:
@AnalyzeClasses(packages ="fr.tristan.demoassurance", importOptions = {ImportOption.DoNotIncludeTests.class, ExcludeConfig.class}) public class ArchitectureTest { @ArchTest public static final ArchRule onionArchitectureRule = onionArchitecture() .domainModels("fr.tristan.demoassurance.domain.entity..") .domainModels("fr.tristan.demoassurance.domain.event..") // 添加domain.spi包到领域服务层 .domainServices("fr.tristan.demoassurance.domain.api..", "fr.tristan.demoassurance.domain.spi..") .applicationServices("fr.tristan.demoassurance.application.controller") .adapter("message", "fr.tristan.demoassurance.infrastructure.message..") .adapter("persistence-mongodb", "fr.tristan.demoassurance.infrastructure.repository.mongodb..") .adapter("persistence-mysql", "fr.tristan.demoassurance.infrastructure.repository.mysql..") ; }
配置完成后,domain.spi下的所有接口都会被归类为领域服务层,允许依赖最内层的领域模型,报错会消失,同时也能保证PoliceAssuranceRepositorySpi的依赖符合架构规则。
内容的提问来源于stack exchange,提问作者Tristan
相关产品推荐
相关产品推荐

