5

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

 3 years ago
source link: https://www.phodal.com/blog/arch-guard-as-code-architecture-document-as-test/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Posted by: Phodal Huang June 15, 2021, 8:33 p.m.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

架构测试的局限性

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

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

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

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

架构守护 DSL 示例

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

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

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

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

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

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

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

欢迎加入 Guarding 架构测试与守护:https://github.com/inherd/guarding


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK