实战Arch Unit
在以前的文章中介绍了通过 [《实战PMD》](https://zhuanlan.zhihu.com/p/105585075)、[《实战Checkstyle》](https://zhuanlan.zhihu.com/p/105583516)在代码级守护我们的代码,比通过[《实战Jacoco》](https://zhuanlan.zhihu.com/p/105581725)来了解当前项目的测试覆盖情况。通过得到数据了解我们的项目质量,进行定向的改进。
使用这些简单方面的自动化工具比凭空猜想或者全靠人力来接发现代码上的问题,效率高多了。
这篇文章将聚焦在`Arch Unit`上,`Arch Unit`能通过为我们提供架构的守护。
1. 开发前的准备
2. 项目分层检测
3. 循环依赖检测(同一个package下,不同package下的循环依赖)
4. Package依赖检测
5. Package和Class的包含关系检测
6. 忽略某些违规行为的三种凡是
7. 如何组织Arch Unit的测试
先来看一下Arch Unit的相关功能介绍。
这些功能很好,但是要是面面俱到,那么维护、查看规则也是一件麻烦事,所以针对项目情况,有选择定制,才能更好的展现器价值。
通过自己坐在项目的情况,可以通过金字塔来罗列:哪些行为做了价值大,哪些事情做了价值小。
---
### 1,开发前的准备
`Arch Unit`集成了`Junit4`和`Junit5`,在它的模块中包含:`archunit`、`archunit-junit4`、`archunit-junit5-api`、`archunit-junit5-engine`、`archunit-junit5-engine-api`。
在项目中只需要引入测试相关的JUnit5相关的依赖。
dependencies { testCompile 'com.tngtech.archunit:archunit-junit5:0.13.1'} test { useJUnitPlatform()}
实践过程中有可能遇到的情况:
`Tips 01`: 当 @Analysis 中配置的 package 目录写错时,并不会报错package不存在,而是会让全部测试通过。
`Tips 02`: *在 layer 验证的时候,定义 layer的时候,package 名称需要根据需要在包名后添加 "..",例如:
layeredArchecture() .layer("Representation").definedBy("com.page.practice.representation..") .layer("Domain").definedBy("com.page.practice.domain..") ... .whereLayer("Domain").mayOnlyAccessedByLayers("Representation") ...
其中的 `com.page.practice.representation..` 结尾使用 ..
,原因是 `representation` 中如果包含了其他两个package 例如:`request` 和 `response`,那么当 `request` 中调用到了 `domain` 中类后,上面的代码是可以检测通过。
如果去掉 `com.page.practice.representation..` 结尾的 ..
,那么当`request` 中调用到了 `domain` 时,检测是不过的。
---
### 2, 项目分层检测
在做`DDD`的一些落地项目中我们会使用四层架构,即`Representation`、`Application`、`Domain`、`Infrastructure`四层,这四层的调用关系如下图所示。
下面通过一个例子我们来约束这几层的调动关系。
package com.page.practice.archunit; import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.lang.ArchRule; import org.assertj.core.presentation.Representation; import static com.tngtech.archunit.library.Architectures.layeredArchitecture; @AnalyzeClasses(packages = "com.page.practice") public class LayeredArchitectureTest { @ArchTest static ArchRule layer\_dependencies\_are_respected = layeredArchitecture() .layer("Representation").definedBy("com.page.practice.representation..") .layer("Application").definedBy("com.page.practice.application") .layer("Domain").definedBy("com.page.practice.domain..") .layer("Infrastructure").definedBy("com.page.practice.infrastructure") .whereLayer("Representation").mayNotBeAccessedByAnyLayer() .whereLayer("Application").mayOnlyBeAccessedByLayers("Representation") .whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Representation"); }
如上代码,定义了四层 layer,然后定义了:
1. `Representation` 不能被其他 Layer 调用,
2. `Application` 能够被 `Represenatation` 调用
3. `Domain` 能够被 `Representation`、`Application` 调用。
`TIPS:`
其中的 `com.page.practice.representation..` 结尾使用 ..
,原因是 `representation` 中如果包含了其他两个package 例如:`request` 和 `response`,那么当 `request` 中调用到了 `domain` 中类后,上面的代码是可以检测通过。
如果去掉 `com.page.practice.representation..` 结尾的 ..
,那么当`request` 中调用到了 `domain` 时,检测是不过的。
---
### 3, 循环依赖检测
对于一些项目仍旧在使用`MVC`三层的结构,当项目进行一段时间后,经常会遇到的循环依赖的问题。
而解决这类问题除了参考`DDD`等实践还有可能会根据团队对项目的理解,而添加团队规范,形成约束,如果只是口头约束,那么接下来开发过程中还会遇到多个上下的循环依赖问题,而如何通过测试代码约束住循环依赖,并在运行测试时第一时间得到反馈,这是效率较高的做法(`Tips`:使用 自动化测试
比完全启动项目 人肉点击测试
效率要高很多)。
在日常工作中经常出现两种循环依赖,一种是同一个`package`下出现循环依赖,另一种情况是不同`package`下的类出现了循环依赖。`Arch Unit`对这两的场景约束验证略有不同。
(1)场景一:相同`package`下的类出现了循环依赖
package com.page.practice.application; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.library.dependencies.SliceAssignment; import com.tngtech.archunit.library.dependencies.SliceIdentifier; import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices; @AnalyzeClasses(packages = "com.page.practice") public class CycleDependencyRulesTest { private static SliceAssignment legacyPackageStructure = new SliceAssignment() { [@Override](https://my.oschina.net/u/1162528) public SliceIdentifier getIdentifierOf(JavaClass javaClass) { if (javaClass.getPackageName().startsWith("com.page.practice.application")) { return SliceIdentifier.of(javaClass.getName()); } return SliceIdentifier.ignore(); } @Override public String getDescription() { return "legacy package structure"; } }; @ArchTest static final ArchRule no\_cycles\_by\_method\_calls\_in\_same_slice = slices() .assignedFrom(legacyPackageStructure) .should() .beFreeOfCycles(); }
上面的代码关键部分在 `getIdentifierOf()`中,如果`package`名字是以`javaClass.getPackageName().startsWith("com.page.practice.application")`则`return SliceIdentifier.of(javaClass.getName());` 认为是不同slice,如果不是目标`package`则忽略。
**(2)不同`package`下的类出现循环依赖
使用Arch Unit对不同`package`下的循环依赖验证条件要相对简单,只需要在`matching()`方法中,指定匹配的`package`规则就可以了。
package com.page.practice.application; import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.lang.ArchRule; import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices; @AnalyzeClasses(packages = "com.page.practice") public class CycleDependencyRulesTest { @ArchTest static final ArchRule no\_cycles\_by\_method\_calls\_between\_slices = slices() .matching("application.(*)..") .should() .beFreeOfCycles(); }
即:在包 `com.page.practice.infrastructure` 中的内类不能够调用 `com.page.practices` 中的类。
---
### 4, Package依赖检测
在实现`DDD`的四层架构中,`application`可以依赖`infrastructure`,但是`infrastructure` 并不能依赖 `application`,所以通过`Arch Unit`建立规则
模拟场景如图,下图中的红线部分这个场景中出现 `infrastructure` 逆向调用了 `application` 层的代码
通过如下代码我们建立约束。
package com.page.practice.archunit; import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.lang.ArchRule; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; @AnalyzeClasses(packages = "com.page.practice") public class PackageDependencyTest { @ArchTest static ArchRule infrastructure\_should\_no\_dependecny\_on_application = noClasses() .that() .resideInAPackage("..infrastructure") .should() .dependOnClassesThat() .resideInAPackage("..application"); }
即:在包 `com.page.practice.infrastructure` 中的内类不能够调用 `com.page.practices` 中的类。
注意这里使用的是 `noClasses()` 静态方法,表达的不能依赖。
---
### 5, Package和Class的包含关系
约束某个`package`下的类的命名规则也是非常重要的,例如之前有的项目在进行过程中,由于没有进行自动化约束,而是人为的传授约束,结果一段时间过去后,命名五花八门。
例如在微服务中使用到了 `Feign`,结果出现了如下命名方式:`xxFeign`,`xxFeignClient`、`xxClient`、`xxService`。
预期较劲脑汁的沟通,不如建议一套自动化约束。了解自卸自动化约束是修改、添加代码的前提之一。
下面的例子是在**.application 包下的类都需要以Service结尾。
@ArchTest static ArchRule = classes() .that() .resideInAPackage("..application") .should() .haveSimpleNameEndingWith("Service");
### 6, 忽略某些违规行为
如果你的项目已经开始,且代码质量不高,直接添加这些`Arch Unit`约束,很可能会遇到进退两难的问题,所以临时忽略一部分是架构向好的反向演进过程中一种需要使用的方法。
有两种行为能够忽略部分规则
(1) 使用@ArchIgnore
注解
@ArchIgnore @ArchTest static ArchRule = classes() .that() .resideInAPackage("..application") .should() .haveSimpleNameEndingWith("Service");
(2) 通过制定更为具体的`package`名称来做局部限定,在对自己何时的时候在扩大范围
(3) 在classpath下使用`arch_ignore_patterns.txt`,并在文件中添加需要忽略的`package`或者`class`
.\*some\\.pkg\\.ABC.\*
所有符合`some.pkg.ABC`都会被忽略。
### 7, 如何组织Arch Unit的测试
为了方便维护,可以一个类型的规放置在一个单独的Test文件,当查找是能够方便的进行相关规则的查找。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
终极武器-阿里云VMware无代理迁移发布增量迁移功能
前文说到,阿里云提供了针对VMware的无代理整机迁移服务,试用下来亮点颇多,可以极大地提高VMware用户上云的效率。 但是考虑到大部分需要迁移的用户都有指定的切换窗口,需要迁移的时间更加可控,并且还有会有需要临时切换做验证的需求,开发团队紧接着就发布了增量迁移的功能。增量迁移,就是在首次全量数据同步完成的基础上,根据设置的同步频率自动把一段时间的增量数据同步上云上,这样客户在需要的时候可以直接做切换,切换时间精度从原来的小时级别,提升到了现在的分钟级别,可谓是迁移的终极解决方案了。下面我们来试试看这个新的”终极武器“。 正文 首先是部署激活网关,这一步可以参考前文,我们直接进入正文,进入创建迁移计划的页面:在这里我们启动增量功能,并设置同步周期为12小时,然后选择待迁移的虚拟机,并设置云上的配置后,即可启动迁移任务。 需要注意的是,如果要启动增量同步功能,必须在vCenter里对每台虚拟机单独启用cbt功能,可参考:https://kb.vmware.com/s/article/1031873。否则每次会转为一次全量的同步。 任务启动后,在迁移状态中可以看到: 针对每台机器的迁移,...
- 下一篇
Apache Jackrabbit 2.21.0 发布,可扩展、高性能分层存储库
Apache Jackrabbit 2.21.0 发布了。Jackrabbit Oak 是一种可扩展的高性能分层内容存储库,旨在用作现代世界级网站和其它要求苛刻的内容应用程序的基础。 此版本更新内容: Task [JCR-4491]-将 Commons VFS 升级到 2.6 [JCR-4516]-交换机 bundlecomparisonVersion为 2.20.0 [JCR-4519]-将 httpcore 依赖关系更新为 4.4.13 [JCR-4520]-将 Jackrabbit 主干和 2.20 更新为 Oak 1.22.0 [JCR-4522]-将 httpclient/mime 依赖项更新为 4.5.11 [JCR-4525]-将 Jackrabbit 主干和 2.20 更新为 Oak 1.24.0 [JCR-4529]-将 tomcat 依赖项更新为 8.5.50 [JCR-4530]-jackrabbit-core:避免使用已弃用的 Commons-Collection 缓冲区 更新说明: http://mail-archives.apache.org...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Docker使用Oracle官方镜像安装(12C,18C,19C)