例如项目结构如下:
- com.myapp.controller SomeControllerOne.class SomeControllerTwo.class - com.myapp.service SomeServiceOne.class SomeServiceTwo.class - com.myapp.persistence SomePersistenceManager例如我们规定:
包路径com.myapp.controller中的类不能被其他层级包引用。
包路径com.myapp.service中的类只能被com.myapp.controller中的类引用。
包路径com.myapp.persistence中的类只能被com.myapp.service中的类引用。
编写规则如下:
layeredArchitecture() .layer("Controller").definedBy("..controller..") .layer("Service").definedBy("..service..") .layer("Persistence").definedBy("..persistence..") .whereLayer("Controller").mayNotBeAccessedByAnyLayer() .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") .whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service") 循环依赖关系检查例如项目结构如下:
- com.myapp.moduleone ClassOneInModuleOne.class ClassTwoInModuleOne.class - com.myapp.moduletwo ClassOneInModuleTwo.class ClassTwoInModuleTwo.class - com.myapp.modulethree ClassOneInModuleThree.class ClassTwoInModuleThree.class例如我们规定:com.myapp.moduleone、com.myapp.moduletwo和com.myapp.modulethree三个包路径中的类不能形成一个循环依赖缓,例如:
ClassOneInModuleOne -> ClassOneInModuleTwo -> ClassOneInModuleThree -> ClassOneInModuleOne编写规则如下:
slices().matching("com.myapp.(*)..").should().beFreeOfCycles() 核心API把API分为三层,最重要的是"Core"层、"Lang"层和"Library"层。
Core层APIArchUnit的Core层API大部分类似于Java原生反射API,例如JavaMethod和JavaField对应于原生反射中的Method和Field,它们提供了诸如getName()、getMethods()、getType()和getParameters()等方法。
此外ArchUnit扩展一些API用于描述依赖代码之间关系,例如JavaMethodCall, JavaConstructorCall或JavaFieldAccess。还提供了例如Java类与其他Java类之间的导入访问关系的API如JavaClass#getAccessesFromSelf()。
而需要导入类路径下或者Jar包中已经编译好的Java类,ArchUnit提供了ClassFileImporter完成此功能:
JavaClasses classes = new ClassFileImporter().importPackages("com.mycompany.myapp"); Lang层APICore层的API十分强大,提供了需要关于Java程序静态结构的信息,但是直接使用Core层的API对于单元测试会缺乏表现力,特别表现在架构规则方面。
出于这个原因,ArchUnit提供了Lang层的API,它提供了一种强大的语法来以抽象的方式表达规则。Lang层的API大多数是采用流式编程方式定义方法,例如指定包定义和调用关系的规则如下:
ArchRule rule = classes() // 定义在service包下的所欲类 .that().resideInAPackage("..service..") // 只能被controller包或者service包中的类访问 .should().onlyBeAccessed().byAnyPackage("..controller..", "..service..");编写好规则后就可以基于导入所有编译好的类进行扫描:
JavaClasses classes = new ClassFileImporter().importPackage("com.myapp"); ArchRule rule = // 定义的规则 rule.check(classes); Library层APILibrary层API通过静态工厂方法提供了更多复杂而强大的预定义规则,入口类是:
com.tngtech.archunit.library.Architectures目前,这只能为分层架构提供方便的检查,但将来可能会扩展为六边形架构\管道和过滤器,业务逻辑和技术基础架构的分离等样式。
还有其他几个相对强大的功能:
代码切片功能,入口是com.tngtech.archunit.library.dependencies.SlicesRuleDefinition。
一般编码规则,入口是com.tngtech.archunit.library.GeneralCodingRules。
PlantUML组件支持,功能位于包路径com.tngtech.archunit.library.plantuml下。
编写复杂的规则一般来说,内建的规则不一定能够满足一些复杂的规范校验规则,因此需要编写自定义的规则。这里仅仅举一个前文提到的相对复杂的规则:
定义在controller包下的Controller类的类名称以"Controller"结尾,方法的入参类型命名以"Request"结尾,返回参数命名以"Response"结尾。