文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

分层架构演化:从单体的插件化演化所引起的思考

2024-12-03 08:56

关注

最近,在为 Coco 优化分层架构之时,我陷入了各种决策困难之中。所以我通过不断地延迟决策,以摸清更适合现有系统的现状。换个简单来说,在危险边缘徘徊,以期待能获取最大的收益。

在设计新的架构时,我们总会凭借原先的经验,并结合业务现状的需求,并根据未来的需求做出我们的设计。即:

种种因素的影响之下,它注定了我们无法设计一个满足所有历史时期的系统。未来会变成现在,现在会变成过去。

Coco 架构设计:从过去到过去的未来

原先对于 Coca 的各种设计问题,以及 Golang 对于多平台的支持问题等多方面的因素。迫使 Inherd 开源小组在 Coco 在初始阶段,便考虑了为 Coco 设计插件系统。直到最近,我们实现了插件系统之后,发现了原来设计的分层架构已经不满足现今的需求。

虽然,我已经知道新的分层架构应该如何设计,但是我并不想朝那个方向过去。我走走弯路,再看看是否存在一些更有意思的设计。

原始形态:单体架构

在设计初期,我在 Coco 中引入了类似于 Clean Architecture 的分层架构设计(不包含 Cargo 模块):

在 domain 目录下,根据了我们的四大基本业务,进行了二次划分 :

尽管,我一直在说我采用的是类似于 Clean Architecture 的分层架构。但是实际上,并没有采用其中一些重要的设计,比如说通过依赖反转来控制流向的问题。从个人的角度来看:

  1. 它带来一定的架构复杂度,需要不断地传递相关的架构知识,能否在开源项目中推广,有待商榷。
  2. 后续可以通过重构来转换。我并非非常资深的架构专家,所以以学习为出发点更方便。

作为一个单体应用,这个分层结构凑合着:

  1. 不算太复杂,还能让开发人员知道哪的代码往哪里放。
  2. 可以按需演化为 Clean Architecture。
  3. 模块可以进一步按业务拆分。

故事的开始还是蛮美好的。

复用形态之模块化

为了在多个不同的系统/应用之间(即 Coco 项目的代码提供给其它应用)复用代码 ,系统中产出一些独立的模块,如 psa、framework 等等。这也是一个非常常见的模块化的场景。模块化在不同的语言里都有一定的相似之处。

譬如:在使用方式上存在本地使用和远程发布两种模式。在本地使用时,无需关注语义化版本等一系列的事项,只需关注于代码本身。一旦时机成熟,也就可以进化为可远程发布的模块。

从单体中出现模块化的一种典型形式便是,在代码库中以与源码同级的目录呈现。如下:

  1. ├── framework 
  2. ├── psa 
  3. ├── src 
  4. │   ├── app 
  5. │   ├── bin 
  6. │   ├── domain 
  7. │   ├── infrastructure 
  8. │   └── lib.rs 

这里的 framework 和 psa 便是独立的模块,一旦其与其它模块的依赖关系解耦开来,那么它就可以作为独立的应用发布。

复制 over 复用

顺便提一句,对于模块化的代码复用来说,如果代码量较少,那么可以尝试复制一份代码,而不是复用做代码。这样一来,我们可以通过此来解耦依赖。

插件化的架构演变

同时,为了灵活地扩展系统的功能,我们设计了插件系统。(事实上,更多地从意图上,我们只是为了减少包体积大小,这样可以方便地从 GitHub 下载)

于是乎,我们创建了独立的 plugins 目录,并在其中创建了对应的模块,如下的 coco_xxxx 即是插件。同时,我们使用了 plugin_manager 来作为插件的管理器(事实上,后面证明了,这个 manager 不应该独立作为一个模块存在):

  1. ├── framework 
  2. ├── plugin_manager 
  3. ├── plugins 
  4. │   ├── coco_container 
  5. │   ├── coco_pipeline 
  6. │   ├── coco_struct 
  7. │   └── coco_swagger 
  8. ├── psa 
  9. ├── src 
  10. │   ├── app 
  11. │   ├── bin 
  12. │   ├── domain 
  13. │   ├── infrastructure 
  14. │   └── lib.rs 

从设计和演进的角度来看,问题并不多,也可以使用。

演进:未来的未来

好了,由于经验上的不足,我们就面临了之前没考虑到的问题。

提取核心模型

从设计思路上来看,我们本应该在原先的架构模型中,提供一个 core 模块。而在这个 core 模块里呢,则用于提供一些核心的代码给插件和应用。

所以,很快地我们就创建了一个 core_model 出来了。我的本义也就只是提供一个核心模型。我不想像一些插件化项目中,在 core 中提供大量非核心的代码。

只是呢,随着第一个模型复用需求的出现,很快地就有了第二部分、第三部分。

再次抉择:基础设施层的改造

而插件之间除了模型的复用,还会有基础设施的复用。而这些代码,我又不想放到 core 里,所以就又需要抽取中一个 infra 的模块,用来共享基础设施的代码。那么问题来了,我们应该如何选择?

  1. 将原有的 infrastructure 提取到主目录下,作为单独的模块存在。
  2. 双层infrastructure,即只提取共用的代码,到主目录下,作为独立的模块。

从架构设计的思想来看,我是支持双层基础设施的存在。过多的无意识地复制这些公共代码,会导致这个包大小的进一步膨胀。一个典型的例子,就是我们在一个被称为 common 包的 jar 包里,看到一个 common 子包下,还有 common 目录的存在,即 xxx-common.common.common。

小结:架构的持续演化

故事就到这里了。哪怕一个再小的项目,它的架构模式也会随着系统的开发,不断地演化。如果不加以控制,那么系统可能会推动控制。而演进本身呢,也不会是一帆风顺的。

不过,我在思考一个新的东西,关于『分层架构适应度函数』。

Yiki:分层架构适应度函数

无论是在 Coco 还是在 Coca 里,我们都在尝试对系统的分层进行一个评级。而这个评级的其中一个依据是通过依赖关系,来确认各个模块之间的引用关系,从而判断系统的分层架构是否是符合需求的。

通过解析模块之间的引用关系,可以帮效地帮助我们厘清系统模块之间的合理度。

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

 

来源: phodal内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯