Redian新闻
>
架构即代码:编码下一代企业(应用)架构体系

架构即代码:编码下一代企业(应用)架构体系

科技

架构即代码,是一种架构设计和治理的思想,它围绕于架构的一系列模式,将架构元素、特征进行组合与呈现,并将架构决策与设计原则等紧密的与系统相结合。

如我的上一篇文章《为“架构”再建个模:如何用代码描述软件架构?》中所说,要准确描述软件的架构是一件颇具难度的事情。仅就实现的层面来说,也已经很难通过一个标准模型来让所有人达成一致,“哦,这就是架构”。也因此,在无法定义架构的情况下,也很难无法给出一个让所有人信服的架构治理模型。毕竟:模型只有合适的,永远没有对的

( 示例代码见:https://github.com/archguard/archdoc-poc )

但是呢,我们(ArchGuard Team)依旧会在 ArchGuard 构建出一个架构模型,以及架构治理模型,作为推荐的 “最佳实践”。除此,我们还应该提供一种自定义企业应用架构的可能性,这就是架构即代码。面向初级架构师来说,他们只需要按照 ArchGuard 的最佳实践来实施即可;面向中高级架师,他们可以基于 ArchGuard 提供的插件化能力 + DSL 构建自己的架构体系。

所以,如你在其它系统中所看到的那样,要提供这样的能力,需要一定的编码、配置等。所以,我们就需要构建一个架构即代码的系统。那么,问题来了,即代码又是什么鬼。

架构即代码是什么?

在先前的一系列的代码化(https://ascode.ink/)文章中,描述了如何将软件开发完全代码化,包含了将文档、需求、设计、代码、构建、部署、运营等变成代码化。设计和实现一个领域特定语言并不难,如《领域特定语言设计技巧》一文中所描述的过程,在这个上下文之下就是:

  1. 定义呈现模式。寻找适合于呈现架构的方式,如 UML 图、依赖图、时序图等。

  2. 提炼领域特定名词。一系列的架构相关元素,如架构风格:微内核等、架构分层:MVC 等。

  3. 设计关联关系与语法。如何以自然的方式来关联这些架构元素,如关键词、解析占位符等。

  4. 实现语法解析。除了实现之后,另外一种还要考虑的是:如何提供更灵活的扩展能力?

  5. 演进语言的设计。版本迭代

也因此,我们将架构即代码定义为:

架构即代码,是一种架构设计和治理的思想,它围绕于架构的一系列模式,将架构元素、特征进行组合与呈现,并将架构决策与设计原则等紧密的与系统相结合。

接下来的问题就是,如何将这个理念有机的与系统结合在一起?并友好地提供这样的 API 接口(DSL)?

于是放到当前 ArchGuard 的 PoC,架构即代码的呈现方式是 “ArchDoc”,一种基于 Markdown 的交互式代码分析和治理方式。即所有的 “代码” 都通过 markdown 来管理,优点有一大堆:

  • 使用内嵌 DSL (用语法块管理)表述架构

  • 可以记录系统的架构文档,如架构决策、业务架构等

  • 拥有广泛的解析库,能提供更灵活的定制灵感(Ctrl + C, Ctrl + V)。

  • 自定义 Render

  • 广泛的编辑工具支持

唯一的缺点就是实现这样一个工具并不简单。

架构即代码的特点

不过,我们已经实现了一个简单的 PoC(概念证明)版本,在这个版本里,它的特点是:

  • 显式地描述与呈现架构。

  • 架构文档即是规则

  • 设计、文档与实现一致

当然了,还有各种的可扩展能力(这是一个再普通不过的特点了)。

显式地描述与呈现架构

回到日常里,我们经常听架构师说,“我们的服务采用的是标准的 DDD 的分层架构”。但是,这个分层是不是诸如于 “Interface 层依赖于 application、domain、infrastructure 层” 等一系列的依赖关系?开发人员是否知道这些规则?这些都是问题。所以,一个架构即代码的系统,它应该能显式地呈现出系统中的那些隐性知识。

诸如于,我们应该将分层中的依赖关系,显式地声明写出来:

  1. layered {

  2. prefixId("org.archguard")

  3. component("interface") dependentOn component("application")

  4. 组件("interface") 依赖于 组件("domain")

  5. component("interface") dependentOn component("infrastructure")


  6. 组件("application") 依赖于 组件("domain")

  7. 组件("application") 依赖于 组件("infrastructure")


  8. 组件("domain") 依赖于 组件("infrastructure")

  9. }

PS:请忽视上面 Kotlin 代码中的中文元素,它只是用来说明使用中文描述的可能性。毕竟,开心的话,也可以使用文言文。

结合 ArchGuard 中的 DSL 与可视化工具(这里采用的是 Mermaid.js),就能呈现我们所设计的分层架构:

再再结合一下设计的分层 Linter 工具(正在实现中):

  1. linter('Backend').layer()

一旦分层中的依赖关系错了,就可以在持续集成中阻断这些代码的提交 —— 类似于 ArchUnit 这样的机制。稍有区别的是,你不需要将测试和代码放在代码库中,而是可以统一的去管理它们。

而对于其它一系列的更复杂的规则来说,我们可以自定义它们,并将他们与文档结合在一起。

架构文档即是规则

在这种模式之下,我们还可以将文档与代码相结合 —— 前提是:我们已经编写了一系列的规则。如我们在 ArchGuard 中,针对于不同的场景编写了一系列的规则:

  • SQL,如不允许 select * 等

  • Test Code,用于检测代码中的坏味道

  • Web API ,分析 API 的设计是否 RESTful

  • Layer (待实现),分析代码中的分层实现

  • Arch (待实现),类似于 ArchUnit 或者 Guarding 制定更细的依赖规则

  • Change(待实现),编写自定义的变更影响范围规则,如某个类不应该被其它的变更影响到

有了基本架构文档规范之后,我们可以规则化它们,并结合到一起。如下是一个结合 Checklist 和规则的列表示例:

  1. - [x] 不应该存在被忽略(IgnoreDisabled)的测试用例 (#no-ignore-test)

  2. - [ ] 允许存在重复的 assertion (#redundant-assertion)

#no-ignore-test 对应于正在实现的 ArchGuard 中的规则,而 GFM 的 Checklist 中,如果 check 了,则可以表示为开启规则;如果没有 check,则为不开启。前面的文字部分,则是对应的规则描述,与传统的 linter 相比较,略显灵活。

而不论是编写文档还是阅读文档的人,他们可以很轻松地构建起对应的上下文。

设计、文档与代码一致

有了设计和文档之后,就需要结合到已有的代码中,让三者保持一致和准确。在我们的场景之下,就是 ArchGuard 已有的 API,它包含了:

  1. 创建对于代码仓库的分析

  2. 分析代码的语法和构建工具、变更历史等

  3. 分析代码是否满足规则等

如下是 ArchGuard 中对于 repo 设计的 DSL(基于 Kotlin),用于创建代码仓库的分析:

  1. repos {

  2. repo(name = "Backend", language = "Kotlin", scmUrl = "https://github.com/archguard/archguard")

  3. repo(name = "Frontend", language = "TypeScript", scmUrl = "https://github.com/archguard/archguard-frontend")

  4. repo(name = "Scanner", language = "Kotlin", scmUrl = "https://github.com/archguard/scanner")

  5. }

只有三者保持了一致,我们才能确保架构的设计与实现是一致的。

架构即代码是个什么系统?

从实现的层面来说,一个架构即代码系统是一个支持编排的数据系统。原因在于,我们并不想关心数据处理的过程,但是想获取数据的结果,从结果中获取洞见。正如,我们所见到的一个个大数据系统,构建了一个个的可视化能力,以祈祷从中得到洞见。

不过,和祈祷稍有不同的是,我们是带着 N% 可能性的猜想,所以叫做探索。

一种探索式的架构治理

传统的软件开发模型是:编辑-编译-运行(edit-compile-run),这种开发模型的前提是,我们拥有足够的业务洞见。对于一个带着丰富领域知识的业务系统来说,构建这样一个系统并不是一件困难。但是,当我们缺乏足够的领域专家,我们应该如何往下走呢?复杂问题,你只能探索 (Probe) -> 感知 (Sense) -> 响应 (Respond)。

而既然我们本身和很多新生代的架构师一样,也需要探索,也需要分析,然后才是得到结论。那么,我们不妨再尝试切换一下模式。如同,我们构建 ArchGuard 的软件开发模型,也是执行-探索(execute-explore),先从分析一下系统(发布一个分析功能),再配合已有的模式,最后得到 “结论” 或者规则(再发布一个 linter 功能)。

在数据领域,这种方式相当的流行,过去人们用 IPython,现在都改用 Jupyter;另外一个类型则是类似于 RMarkdown 提供的报表式的思路。

  • IPython。is a command shell for interactive computing in multiple programming languages.

  • Jupyter Notebook. is a web-based interactive computing platform.

  • R Markdown。Turn your analyses into high quality documents, reports, presentations and dashboards with R Markdown.

  • D3.js 社区的 Observable。用于 Explore, analyze, and explain data. As a team.

从模式上来说,ArchGuard 更偏向于 RStudio 的模式,只是从社区的资源上来说,Jupyter 相关的实现比较多。

一个经常 OOM 的 “大数据系统”

在我们(ArchGuard core team)的 “数次讨论” 中,最终认为 ArchGuard 是一个大数据分析,而不是简单的数据分析。原因是系统中存在大量的 bug 和大数据相关的(狗头):

  • 存在一定数量的 Out of Memory。

  • 大数据量情况下的可视化优化。

也就是所谓的 ”bug 驱动的架构设计“。

除此,之后另外一个颇有意思的点是,对于更大型的系统来说,它存在大量的新的提交,又或者是新的分支。我们即需要考虑:应对持续提交的代码,构建增量分析的功能

当我们尝试使用大数据的思路,如 MapReduce、Streaming Analysis 相关的模式来解决相关的问题时,发现它是可以 work 的不错的 —— 毕竟都是数据分析。

在 ArchGuard 是如何实现的?

ArchGuard 围绕于 DSL + Kotlin REPL + 数据可视化,构建了一个可交互的架构分析与治理平台。因为还在实现中,所以叫下一代。

1. 提炼架构元素

上文中的(https://ascode.ink/)系列中,也包含了两个架构相关的工具,一个是代码生成 DSL:Forming、另外一个则是架构守护 DSL:Guarding。两个 DSL 所做的事情是,围绕特定的规则架构元素组合到一起,这里的架构元素。

如果没有做过,这一个过程看上去是挺麻烦的,实现上有一些颇为简单的东西可以参考(复制):

  • 架构描述语言论文(ADL)。ADL 已经是一个很成熟的领域了,在设计模式火的那个年代,架构模式(《面向模式的软件架构》)也特别的火。

  • 架构相关书籍的目录。一本好的架构书,只需要看目录就能有个索引,所以也就有了基本的架构元素。

  • 架构的模式语言。模式语言所呈现的是模式之间的关系

  • ……

仅仅是复制那多没意思,要是能自己做做抽象,也是一种非常好玩的事情。

2. 构建插件化与规则分析

如上所述,在 ArchGuard 中,我们尝试以一系列的规则,构建系统的规则,而这些规则是以插件化的形式暴露的。

这就意味着,这样一个系统应该是支持自定义的插件化能力,它即可以让你:

  1. 接入一个新的语言

  2. 编写新的规则

  3. 构建新数据的 pipeline

在 ArchGuard 中还需要改进的是,提供一种元数据的能力。

3. 抽象 DSL 作为胶水

从实现层面来说,为了支撑粘合的能力,我们目前计划设计了三种能力的 DSL:后端架构查询 DSL、架构 DSL、特征 DSL

后端架构查询 DSL

类似于 LINQ (Language Integrated Query,语言集成查询)封装 CRUD 接口,以提供编译时类型检查或智能感知支持,在 Kotlin 中有诸如于:KtOrm 的形式。如:

  1. database

  2. .from(Employees)

  3. .select(Employees.name)

  4. .where { (Employees.departmentId eq 1) and (Employees.name like "%vince%") }

  5. .forEach { row ->

  6. println(row[Employees.name])

  7. }

像一个编程语言编写,可以提供更友好的语法性支持。

架构 DSL

即架构描述语言(Architecture Description Language),以提供一种有效的方式来描述软件架构。

特征 DSL:分析、扫描与 Linter

即封装 ArchGuard Scanner、Analyser、Linter 等,用于构建系统所需要的基础性架构特征。

4. 构建可交互的环境

两年前,在与众多的 Thoughtworker 一起构建 Ledge 的时候,我们就一直在强调文档代码化,并提供可交互的文档环境。在 Ledge 里,你可以使用 Markdown 来绘制各类的图表,只需要借助声明图表类型,示例见:https://devops.phodal.com/helper 。

从模式上来说,ArchGuard 更像是一个 RStudio + Jupyter 的结合版,即提供了大量自定义图形 + 组件能力的 REPL。

在 REPL 上,由于我们计划使用 Kotlin 构建 DSL,所以需要寻找的是 Kotlin 的 REPL。Kotlin 官方创建的 kotlin-jupyter 便成为了一个很好的参考,可惜还没有用得上。与此同时,Kotlin 在设计初期就有了 Kotlin Scripting 的场景,所以其实 kotlin-scripting-compiler-embeddable 就能满足需求。于是,在 PoC 里,我们参考了 Apache Zeppelin 引入了 Kotlin REPL,并创建了一个 WebSocket 作为服务。

在可视化上,稍微复杂一些,需要构建一个 Markdown 解析器、Block 编辑器等。我们暂时采用了 Mermaid.js 作为可视化的图形库之一,另外的还有 D3.js、Echarts 也是其中之一。剩下的问题,便是如何通过 DSL 来整合它们?构建前后端的数据模型是一个临时的方案?

PoC 示例见截图:

5. 依旧是一个 PoC

在这里,ArchGuard 的交互性分析,依然只是一个 PoC(概念证明),但是在不远的将来,你就可以在 ArchGuard 中使用它了。

其它

构建一个这样复杂的工具,并不是一件容易的事。欢迎加入 ArchGuard,一起学习架构和架构治理,还有开发一个纯技术驱动的开源软件。

如果你想实践以下的技术,手把手教你学会:

  1. 编译器前端。对设计和实现 DSL 有兴趣

  2. 编译器周边。Kotlin 的编译器使用

  3. ……

当然,如果你也感兴趣于:

  1. 改进一个遗留系统。重构和设计 ArchGuard 的前端、后端。

欢迎加入 ArchGuard

GitHub:https://github.com/archguard/archguard

Gitee: https://gitee.com/archguard/archguard 

扫描以下二维码,关注 ArchGuard~~


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
《中小企业合规管理体系有效性评价》团体标准发布,你的企业合规了吗?郑州部分停工楼盘业主被赋红码:曾和银行储户有交集,转绿码需写保证书如何做好“防御性编码”?Science:宾大推动CAR-T创新,走向癌症和其他疾病的应用;充分运用了mRNA编码和LNP两项新冠疫苗的关键技术中西方医疗按摩(Medical Massage)对比在危机面前,企业要检视自己的企业架构架构自治服务:构建数据驱动的架构洞察苹果招聘AR/VR UI框架工程师,强调AR/VR可推动下一代交互体验英国公布年逾70“白金禧一代”重磅调查报告:得益于房子,他们成了最富有的一代人...盘点|2022年4月非编码RNA研究领域新进展如何在 Fedora Linux 中安装多媒体编码器 | Linux 中国福山认为俄罗斯在乌克兰将会突然崩溃Flink & 低代码:为应用实时计算铺平道路TeamCode黄超:随时随地编码,低成本提升研发效能一代人有一代人的词典为“架构”再建个模:如何用代码描述软件架构?GAU-α:尝鲜体验快好省的下一代Attention红帽宣布 RHEL 9:企业 IT 的下一代骨干系统 | Linux 中国人性和智慧,这个系列的防伪编码(《南北归一》季总结)架构工作台:构建企业(应用)架构的数字孪生代码覆盖率在性能优化上的一种可行应用2022.03.16 我的心在哪里?我这几天断断续续在听这个,蛮有启发的:《老去的勇气》【微报告】低代码/零代码平台应用实践与趋势研究:制造业篇 | 甲子光年下一代机器人,将承载5G应用的爆发吗?一代人有一代人的失业今日开营!8位海内外专业大咖齐聚,14天试听加码:5位驻场导师多维度答疑,2场职场人分享第四范式陈雨强:企业智能决策的下一代技术「强化学习 + 环境学习」FactorVAE:基于变分自编码器的动态因子模型建设下一代 Web 开放技术——WebContainer提升Java字符串编码解码性能的技巧今年的谷歌I/O大会超「硬」,还展示了下一代AI模型下一代是最重要的投资!为了孩子,我做出了这个选择……宋方金:编剧与责编之间需要更良好的沟通机制和项目机制凉拌牛百叶
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。