一个软件系统,随着时间的推移,总会出现代码腐朽,架构变的越来越没有样子,代码坏味道逐渐增多,技术债务居高不下,失去了最开始的光辉,渐渐的被人称为“遗留系统”。
口耳相传的架构
在大型项目中工作过的发人员可能都经历过这样一件事,项目开始有一些漂亮的架构图,展示了系统应该包含的组件,以及它们应该如何交互。
在敏捷团队中还有敏捷实践,来尝试拉齐团队的成员认知,例如:每天的CodeReview会和团队的开发小伙伴来看每天生产的代码,这个过程我们会聊到这个组件应该放到Domain包下,这个组件不应该调用Application包下的组件,这个对象的后缀名不对,这个包下的不能有Setter方法等等。随着团队成熟度提高这些问题会变得越来越少。
但是当项目变得大起来,新功能更复杂,新成员加入和核心开发人员离开后,新功能可能会以任何适合的方式添加。每一个变化都可能对任何其他组件产生不可预见的影响。尤其是拆分成不同的Team负责不同的微服务,知识传递在这个过程中可能会出现问题,因为架构师的角色可能是分布式的,他无法分身去照顾到所有Team。由于为知识传递不及时,代码生产量又变的很多,reivew也不及时,会突然出现奇怪的结构,将功能写到为完全不合适的组件结构中,到最后架构变成了形同虚设。这就是我们常说的口耳相传的架构,如果核心人员逐渐离去,系统会变得非常脆弱。
自动化功能测试
我们都知道人是不靠谱的,如果靠人来保证一些事情,总会出现一些问题,例如:一个软件系统的功能是否正确如果全靠测试人员的人肉测试,这个系统早晚会垮掉,因为随着时间的推移,人力成本会变高,开发人员也不敢随便加东西,因为一旦加一些功能,可能就需要测试人员从头到尾回归一遍(因为测试不确定开发是不是真改了这一块),为了避免这种问题的出现,减少人力成本,引入自动化测试这个方法论,开发人员TDD开发功能,测试人员自动化脚本回归功能,这种一劳永逸的事情逐渐盛行起来,尤其是在一个稳定不断迭代新功能的系统,极大的保证了开发功能的准确性,减少了BUG的数量。
自动化架构约束测试
既然功能可以通过自动化测试来保证软件系统的正确性,那么软件架构的约束肯定也可以通过自动化测试来保证,随着系统的演进,架构约束规则也可以相应的演进,通过自动化的规则确保开发人员/架构师的共识,来提高代码库的质量并防止开发速度下降。新开发人员也可以更轻松的熟悉代码并加快开发速度,因为一旦违约,自动化测试就可以为你把关。
archunit 就是其中一个把这种思想实现的框架,它可以检查包和类、层和层之间的依赖关系,检查循环依赖关系等等。
三层架构
layeredArchitecture()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Persistence").definedBy("..persistence..")
.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
.whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service")
包依赖检查
noClasses().that().resideInAPackage("..source..")
.should().dependOnClassesThat().resideInAPackage("..foo..")
类和包之间的检查
classes().that().haveSimpleNameStartingWith("Foo")
.should().resideInAPackage("com.foo")
除了上面的例子,还可以自定义自己的规则去约束你的架构
总结
自动化架构约束思想就像TDD一样,它改变了传统的知识传递模式,它通过自动化程序去保证架构的准确性,就像单元测试去保证你的功能一样,他永远都是实时的和你的业务功能一样,只要你的规则定义的足够明确,就不用去花大量的时间去传递知识,担心架构出问题。