架构守护代码化:架构文档即测试

开发 架构
架构守护代码化,即使用易于阅读和维护的领域特定语言,来描述软件架构守护的规则,对诸如于分层架构、包访问规则、包数量、继承命名等进行限制。

[[405741]]

架构守护代码化,即使用易于阅读和维护的领域特定语言,来描述软件架构守护的规则,对诸如于分层架构、包访问规则、包数量、继承命名等进行限制。

PS:我们这里所说的代码化,所指的是与领域特定语言的方式进行描述。

早先呢,我只是因为使用 Java 编写的 ArchUnit 不支持其它语言,而在其它语言的生态里呢,也没有这样的合适的工具。所以呢,我就想着在 Uncode 里设计一个全新的架构守护工具,也就是 Inherd 开源小组里的 Guarding:https://github.com/inherd/guarding/,一个多语言的架构守护工具 —— 基于 Tree Sitter 解析各类编程语言。它设计了一套外部 DSL,其借鉴于 ArchUnit 设计的内部 DSL 语法。

架构的腐化:为什么我们需要架构守护?

观察软件架构在开发过程中的变化, 是一件非常有意思的事情。日常,我经常与中大型规模公司的架构师、技术负责人聊天,讨论一些关于架构和规范相关的问题,也是颇有意思的。当我们聊到架构的时候,从聊天的过程来看,都是颇为美好的。但是呢,打开代码库,看到代码的分层实现、代码的一些规范,我们就会发现结果并非如此。

美好的开始。系统在设计的初期,架构师们都根据了自己的能力和经验,对系统进行了快速的“精心”的设计。随后,在迭代的开发中,按自己新捕获到的知识,对系统进行一些调整。如果这些资深的架构师都在编码(间歇性的),又或者是经常性的打开代码库瞧一瞧。那么,自然而然地系统不会出现过大的偏差。所以,我坚信对于有一定规模的软件组织来说,他们对系统都是有着良好的设计。

腐化的架构。系统在中后期开发的过程中,先前的架构师缺乏对于架构的关注,又或者是经历了一些人员的变更,导致了系统出现了一处又一处的架构不一致。当然,其中还有一类典型的原因是,架构相关的文档和规范缺乏了维护。由于这一系列的种种原因,使得我们看到的系统架构与这些架构师原先的预期是不一致的。

基于上述的种种原因,在架构上实施守护便成为诸多架构师要考虑的问题。

为什么需要架构守护代码化?

程序员讨论写文档,也讨厌别人没写文档。

对于架构知识的记载、传播和转换,也是知识传递的范畴。从当前阶段来看,它存在以下几个不同的级别:

  • 系统本身没有架构文档,文档存在于人们的脑海里。
  • 系统存在架构文档,难以理解(没有架构图)。
  • 系统存在架构文档,只在早期创建,但与实际架构不一致。
  • 系统的架构文档持续更新,但是未能及时反应问题。
  • 系统的架构文档持续更新,并使用了架构守护,以确保两者的一致性。
  • 系统的架构文档即系统的架构守护测试。

上述的几点,我想不论是开发者,还是架构师都是深有体会的。

文档化的架构,需要阅读和牢记

在架构设计初期,开发人员对于架构的设计往往都是经历过激励的讨论。所以,每个人对于架构的形态都掌握得差不多,不需要过多的记录也能知晓设计。沉淀下来的文案往往只是一些决策的结果,缺乏过程式的讨论等。

因为这一种种原因,所以在过去的几年里,我们一直在推崇『架构决策记录』(ADR),记录每一项架构上下文、决策和结果等相关的信息。

架构测试的局限性

这是一个老生常谈的问题,所以诸如于在 Java 世界里,人们设计出了 ArchUnit 这样在的工具来守护系统的架构。架构测试作为架构的文档,缺少易读性等等的问题。为了在多个项目中使用,还需要大量地复制和粘贴。

PS:在早期,我也尝试为 JavaScript / TypeScript 世界,设计类似的架构守护工具(即 dilay),但前端世界对于这一类的需要并不迫切。多年后,我又设计了一个新的工具,只是它已经适用于多个语言和框架。

架构守护即代码:架构文档即测试

架构守护代码化,即使用易于阅读和维护的领域特定语言,来描述软件架构守护的规则,对诸如于分层架构、包访问规则、包数量、继承命名等进行限制。

架构守护 DSL 示例

一个好的架构文档是个测试,并且可以执行。如 ArchUnit 设计的内部 DSL 语法:

  1. classes().that().haveSimpleNameStartingWith("Foo"
  2. .should().resideInAPackage("com.foo"

这句话里,描述了一个规则: Foo 开头的类应该放在 com.foo 包下。这也是我们在设计架构的时候,会设计的架构文档。如果我们把它翻译成英语的话,它应该就是:

  1. class(startsWith "Foo")) resideIn "com.foo" 

另外一种潜在的形式可以是:

  1. (startsWith "Foo").class resideIn "com.foo" 

从实现难度上来说,两者的差别并不大,但是显然前者更易于理解和编写。以此类推,我们可以继续设计一系列的规则。

其它

欢迎加入 Inherd Guarding 架构测试与守护。

GitHub: https://github.com/inherd/guarding 。

本文转载自微信公众号「phodal」,可以通过以下二维码关注。转载本文请联系phodal公众号。

 

责任编辑:武晓燕 来源: phodal
相关推荐

2022-09-19 07:03:47

IaCTerratest测试

2019-04-17 09:00:00

DevOps基础架构代码工具

2023-02-19 15:31:09

架构软件开发代码

2020-04-17 19:41:57

基础架构即代码平台即代码云计算

2016-04-14 09:12:20

惠普

2020-09-25 07:00:00

基础架构代码模板

2011-09-14 13:49:32

架构

2023-04-20 08:00:40

2013-07-17 09:40:47

EMC备份存储

2022-08-12 15:47:17

工具基础架构IT

2020-06-18 08:52:37

基础架构即代码

2016-09-01 15:02:38

混合云多云基础架构

2016-08-30 10:39:44

云计算

2017-10-09 17:52:57

MySQL自动化测试GitHub

2012-08-28 13:34:28

架构师软件架构架构文档

2022-11-03 10:29:08

ArchUnitJava字节码

2021-06-29 21:48:32

开源语言架构

2023-08-25 15:44:16

自动化测试软件开发

2009-07-22 18:48:35

2017-12-04 12:49:16

跨国互联网基础设施即代码
点赞
收藏

51CTO技术栈公众号