Polylith:一款新的软件架构

news/2024/5/20 8:17:45 标签: java, serverless, 开发工具
\\

d723b6716021b92e93b1fd0711aecc60.png

\\

Joakim Tendstrand是Clojure和Datomic的架构师和开发人员,最近向作者强烈推荐了自己团队的产品Polylith,这个名字结合了“很多”和“石头”的概念。它是一种软件体系结构,利用许多构件来组合系统,所有构件就像工作在一个地方一样。Polylith克服了单体、微服务、无服务架构的一些缺点,本文希望从概念和实现上对这个新架构做一个介绍,以引起开发使用者的兴趣可以去采纳并应用它。

\\

Polylith的特点是功能简单和可组合性。它可以帮助我们构建简单、可维护、可测试和可扩展的系统。它像乐高(Lego™)一样利用具有共享函数属性的构件块来构建系统。

\\

77af8c48090ba7092b35989843b93a62.png

\\

将构件组合成系统:

\\

9c1ff3f5291af6f8fe5557f78a8fd0f4.png

\\\\

当前架构是否可以改进?

\\

下面对比了开发和生产体验中的三个主流软件架构:

\\
  • 单体(Monolith), 一种软件体系结构,其中代码存储在单个代码库中并部署为单个人工制品。\\t
  • 微服务(Microservice), 一种由小型可独立部署的服务组成的软件体系结构,每个服务都在一个单独的进程中运行,并通过网络与其他网络进行通信。\\t
  • 无服务器(Serverless),一种基于云计算执行模型的软件体系结构,云供应商动态管理机器资源的分配。\

4321dc55f1bad0dff8d1112336662b9b.png

\\

开发体验

\\

712cfdef4aac7b0f9df4e514fabbe689.png

\\

生产体验

\\

这些体系结构提供了大量如何部署系统的指导,但对于如何在每个系统中构造代码指导很少。

\\
  • 现有软件体系结构所经历的偶然的、高度复杂性以及与开发相斥的挫折。\\t
  • 我们想要一个架构,它能给代码库一个可理解、可维护、可重用和可测试的结构。我们想要一个一流的开发体验,当我们作出改变时,能导航和重构所有的代码,并快速反馈。我们希望建立可伸缩的系统,但避免不必要的复杂性被“过早分发”。\\t
  • 我们想让它能简单、快速、有趣地构建任何大小的系统。\

Polylith是什么?

\\

为了帮助解释这个架构,Joakim和他的团队发明了一种隐喻(metaphor),一个基于Clojure的体系结构,还演化了一个开发工具

\\

ed63b2d917935254d8ec3d3dca0eac91.png

\\

完整的Polylith体验

\\
  1. 隐喻帮助理解和交流系统。\\t
  2. 体系结构将系统构造为高级的构件,并创建有效的开发环境来处理所有代码。\\t
  3. Polylith工具使开发者能快速、有趣地与Polylith系统一起工作。\
\\

介绍

\\

7715092469bfe996408f6efb416ffb31.png

\\

隐喻有助于理解Polylith的建筑概念。Polylith系统由三种类型的构件组成:组件(Component)、基底(Base)和(Library)。每种类型的构件都表示打包代码的特定方法,其目标是使它们简单地组合成系统。

\\

基本知识

\\

隐喻建立在两个基本的功能概念上:函数和命名空间。

\\

功能

\\

函数具有许多属性,封装性、简单性、无状态、纯度。这些属性使函数(尤其是纯函数)具有牢固的、可组合和可测试的代码单元。这就是为什么选择在隐喻中使用类似乐高的砖块,因为乐高就是一个牢固的、可组合的积木。

\\

61d3e590dc48c4f6d82f9f682960dc88.png

\\

“乘法”乘以其自变量并返回结果

\\

ce8d6831a003b42b3c3085e0a7b19e78.png

\\

绿色的部位表示函数的实现

\\

a3be8730376cc9a0cd81fdcf9bc730f1.png

\\

红色的部位表示对另一个函数的依赖关系

\\

dd3178040ac1a8d19c6a51a2c8cccbb3.png

\\

底部的方形孔代表函数的签名

\\

签名: 函数的名称、参数和返回结果。

\\

组合函数

\\

让我们来看一个更大的函数,以及函数调用在隐喻中的作用。

\\

4213890bde60b78a80b691299eb54a23.png

\\
  • \"圆形区域\"计算给定半径圆的面积。\\t
  • 绿色部分表示函数的实现。\\t
  • 黄色部分代表了我们的“乘法”函数的依赖性。\\t
  • 底部的圆孔代表函数的签名。\

命名空间

\\

名称空间打包了相关功能,作为一个共同术语并被赋予一个名称。在Java中,这个概念被称为包,在其它语言中,它被称为模块。让我们看看如何将函数打包到Clojure中的命名空间中:

\\

ab5bf95b18e71d782e06e5ed5f091a6a.png

\\

组件

\\

组件是Polylith的核心构件,将系统划分为封装的和可组合的代码块。通过将它们的实现与它们的接口分离来实现它们的封装和可组合性。

\\

04b5ed7076b136a42c96fbbb74b3191a.png

\\

浅绿色层是接口,深绿色层是实现

\\

设计良好的组件具有单一职责、描述性名称,并且公开了一组仅与其职责相关的清晰功能。举几个例子,一个组件可以:

\\
  • 是某领域实体,如用户、帐户或发票\\t
  • 管理与外部系统的集成\\t
  • 处理特定的基础设施,如电子邮件、日志记录或存储\

基底

\\

基底是Polylith的基础构件,是一个系统与外界沟通的门户。基底通过将实现与接口分离来实现封装。

\\

680d62739107d79928b63c78859a27ee.png

\\

浅蓝色层是接口(API),深蓝色层是实现

\\

每一个基底都有一个责任:向外界展示一个系统的功能。在Polylith中,可以在基底中自由地使用任何API技术,例如REST、gRPC、GraphQL或只是简单的旧的命令行。

\\

基底和组件之间的区别是基座具有平的底部。这意味着基底是不可合成的,它们不能被其他构建块所依赖。基底总是位于Polylith的底部。

\\

\\

库(Library)是Polylith的“屋顶”构件,是别人的代码,内部代码依赖于这些代码。

\\

9793aae738f594a8c64ec983408ffdfa.png

\\

系统

\\

Polylith的构件组合方法使得构建系统既简单又有趣。一旦设计好了基底的API,那么只需要组成一组正确的组件和库,这些组件和库就能实现了我们公开的功能。让我们来看看一个完整的系统:

\\

9c1ff3f5291af6f8fe5557f78a8fd0f4.png

\\

系统:一个基,两个组件,三个库

\\

生态系统

\\

生态系统(ecosystem)是Polylith的扩展解决方案。它们是一个系统的集合,为外部世界提供一套统一的功能。在Polylith中,它们是通过一个“电缆”连接的,它从系统B中的一个组件的外部依赖性扩展到系统A的一个端点。

\\

32c4cc836ba2c2bc337f317e7ed2c90f.png

\\

系统B连接到系统A的API提供的端点

\\\\

Polylith架构的设计主要围绕两个目标:简单性速度。简单性来自于保持概念的独立性,速度来自于把我们所有的代码放在一个地方。这一节介绍Polylith是如何利用Clojure来实现这两个目标的,并用一个RealWorld的示例程序来描述(代码参考文章末链接)。

\\

工作空间

\\

工作空间“workspace”是Polylith项目中的根文件夹,这是使用组件组装系统的地方。

\\

可以把工作空间想象成一个办公桌:一个与我们的积木一起工作的地方。它用“抽屉”来保存构件,用“架子”来组装系统,还有一个“工作台”作为开发环境。

\\

2101e01e6a062979705331d6dff4d6f9.png

\\
  • 工作空间就像一个有抽屉、架子和工作台的书桌:\\t
    • “抽屉”是我们保存基底、组件和接口的文件夹。\\t\t
    • “架子”是我们配置和存储系统的文件夹。\\t\t
    • “工作台”是我们保持开发环境的文件夹。\\t
    \

基底

\\

基底“bases”文件夹是一个“抽屉”,它保存了工作空间中的所有基底。

\\

5601a240a5c749eafeb25e8cab41bee0.png

\\

每个基底都有一个标准的Clojure项目结构:使用Polylith.clj文件(类似于pom.xml,被用于Maven和Java),一个readme.md文件,一个资源文件夹,一个src文件夹,和一个测试文件夹。每个基底都是一个项目,可以将它们编译为单个的工件,这保证了它们与所有其他构件解耦。

\\

e43ef65e116335651cceccc09e096d32.png

\\

“api”命名空间公开了所有的基底

\\

组件

\\

组件“components”文件夹是另外一个“抽屉”,它保存了工作区中的所有组件。

\\

82f458e5d0a85fe084aff2c4210590f4.png

\\

RealWorld组件“抽屉”包含八个组件

\\

环境

\\

环境“environments”是存储所有环境的文件夹。每个环境都是一个可以在IDE或代码编辑器中打开的项目。我们可以将环境文件夹想象成一个“工作台”,它可以访问工作空间中的所有环境。

\\

bf953119054b4895db660dd1dea4fca6.png

\\

环境是与构件一起工作的地方

\\

这给了我们选择多个环境的条件,每个环境都有不同的组件和基底。

\\

接口

\\

接口“interfaces”是存储所有“工作空间接口”的文件夹。“工作空间接口”保证构件的实现互不访问,实现组件之间的隔离。

\\

b15fb0d3b9ac765fe2e1e0c22a59a4e6.png

\\

系统

\\

我们可以把系统“systems”文件夹想象成一组“架子”,它把工作区中的所有系统都保存起来。

\\

503d5fdfa7d89073324e009127ba1c93.png

\\

系统将选定的构件组装成整个工件,通过构建和部署多个工件,从单体开发(monolithic在一个环境中拥有所有构件)出发,实现水平可伸缩性的提升。

\\\\

Polylith工具优化Polylith系统的产生、发展、测试和建造。Polylith工具帮助使用者:

\\
  • 创建工作空间、环境、系统、基础、组件和接口的结构\\t
  • 仅仅测试或构建上次成功后改变的构件\\t
  • 查看任何代码改变的状况\

学习Polylith工具的GitHub库,了解它是如何工作的以及如何使用它。如果想快速浏览这个工具的所有特点,从命令部分开始是一个很好的选择。

\\\\

Polylith的优点

\\

让我们把Polylith与之前看的三种结构进行比较。Polylith系统可以作为单个工件(如单体)、作为生态系统中协作的多个工件(如微服务)部署,或者作为无服务器体系结构中的Lambda功能。

\\

开发

\\

Polylith的单一开发环境允许我们在一个地方与所有的构建块一起工作。这将我们的开发经验与我们所选择的部署架构断开连接。

\\

6d6b012a72c777618627a9282bb2c339.png

\\
优点解释
调试Polylith开发环境中的所有代码都可以在单个REPL中运行,提供了一流的REPL驱动的开发和调试体验。
快速反馈Polylith工具跟踪自上次成功测试或构建以来哪些基础和组件已经被更改,并且只进行编译和测试。这给我们在本地开发环境以及持续集成环境中构建和部署提供了闪电般的快速反馈。
重构组件接口通过简单函数调用连接到它们的实现。这意味着可以用IDE/代码编辑器安全地重构接口。
可重用组件是可重用的,因为它们是封装的,无状态且可组合的。它们可以在单个系统和多个系统中重复使用。
简洁性Polylith构件只是代码,是接口后面的函数集合。接口保证封装,这确保了代码库分离,保持更简单的系统。
易测性组件的封装和功能性质使得它们作为完整的系统,易于被隔离测试

生产

\\

Polylith不仅允许延迟部署的决策,还允许我们在需要时轻松地改变部署的架构。这是因为Polylith可以将组件重组成任意数量的系统,这样就可以很容易地部署它们以满足性能需求。

\\

92ab03d9bb90712c0caac4de500f4415.png

\\
优点解释
成本Polylith允许我们避免对解决方案的分布做出过早的设计决定。这使我们避免了“过早分发”,降低了部署复杂度并降低了运营成本。
部署Polylith工具使得构建和部署过程在本地和在我们的CI(持续集成)环境中都简单且无缝。
可伸缩性当Polylith系统没有达到需要的性能/可伸缩性时,那么Polylith使得创建新系统和水平伸缩变得容易。可以重用每个新系统中的现有组件。

实际应用

\\

4fe4dcc69620f1031071b771e3d2f715.png

\\

六个实际应用中的Polylith,所有服务的真实用户

\\

这些项目的规模和复杂度,从一个完整的招聘平台到一个简单的Web应用程序。这些项目告诉我们,组件是一个强大的工具,可以完成复杂的、任何规模的系统。构建像乐高一样的系统比我们能想象的更快、更容易、更愉快!

\\

迁移到Polylith

\\

从一个单体迁移到Polylith,微服务或无服务器架构是相对容易的。这是因为我们可以单独地将每个人工制品迁移到Polylith系统,而不需要改变我们的部署。

\\

从单体架构

\\

不要忘记检查系统编译、构建和所有测试在每个步骤之后通过。

\\
  1. 创建一个新的工作区,并添加一个新的系统,带有一个空的基底。\\t
  2. 将整块代码,包括API和库复制到这个基底中。\\t
  3. 从基底(除了API)中提取所有代码为单个“monolith”组件。\\t
  4. 重构代码来提高系统的质量。从我们的“monolith”组件一次一个组件开始:\

a72c130d4ba285098aea56547344b54c.png

\\

一个有五个组成部分的系统

\\

从微服务架构

\\

微服务是一个由许多小单体组成的体系结构。这意味着迁移到Polylith就像在每个服务上执行单体迁移步骤一样简单:

\\

9c1ff3f5291af6f8fe5557f78a8fd0f4.png

\\

从无服务架构

\\

无服务器是一个由许多Lambda函数组成的体系结构。这意味着迁移到Polylith就像在每个Lambda上执行单体迁移步骤一样简单:

\\

ba798667f1f0232bd87b0025406084ea.png

\\

具有单个端点的Lambda函数

\\

下一步

\\

如果你想了解更多关于Polylith:

\\
  • 如果您已经阅读了所有的文档,但是还没有掌握所有的概念,那么我们建议您观看视频。\\t
  • 如果您通过查看代码学习得最好,然后转向RealWorld示例应用程序。可以克隆这个项目并开始探索,或者只是浏览代码并阅读它的设计。\\t
  • 如果您想讨论Polylith,那么来和我们一起在Polylith论坛上聊天。使用它作为一个空间来打招呼,给出反馈,问问题,分享您的Polylith经验.\

Polylith团队

\\

c856706596f559537962a2c0d089d576.png

\\

感谢张婵对本文的审校。


http://www.niftyadmin.cn/n/835004.html

相关文章

查看端口是否可用

1、使用telnet命令 1、telnet 192.168.31.2 22 2、使用nc命令 2、nc -zv 192.168.31.2 22 nc是netcat的简写-z:设置 nc 只是扫描侦听守护进程,实际上不向它们发送任何数据。-v:启用详细模式转载于:https://juejin.im/post/5ca33abb51882544097e1e4b

Leetcode PHP题解--D37 682. Baseball Game

2019独角兽企业重金招聘Python工程师标准>>> 682. Baseball Game 题目链接 682. Baseball Game 题目分析 给定一个字符串数组,每一个字符串有以下形式: 数字。直接计算得分。。代表本轮分数为上一轮和上上一轮分数之和。D。代表本轮分数为上一…

码流 / 码率 / 比特率 / 帧速率 / 分辨率的区别

码流 / 码率 / 比特率 / 帧速率 / 分辨率 / 高清的区别 2015年03月13日 10:40:30 阅读数:143980 GOP/ 码流 /码率 / 比特率 / 帧速率 / 分辨率 GOP(Group of picture) 关键帧的周期,也就是两个IDR帧之间的距离,一个…

PostgreSQL merge insert(upsert/insert into on conflict) 如何区分数据是INSERT还是UPDATE

背景使用insert into on conflict update语法,可以支持UPSERT的功能,但是到底这条SQL是插入的还是更新的呢?如何判断 通过xmax字段的值是否不为0,可以判断,如果是UPDATE,XMAX里面会填充更新事务号。 注意直…

三星曲面显示器

曲面 三屏 ,开箱、效果实拍 https://post.smzdm.com/p/619599/ 三星S24E390HL显示器内部清灰教程 https://jingyan.baidu.com/article/48a420570b169ea924250407.html 三星C27F591曲面显示器拆解简评 https://www.chiphell.com/thread-1616011-1-1.html转载于:https://www.cnbl…

react 子组件监听props 变化

为什么80%的码农都做不了架构师?>>> componentWillReceiveProps //已经被废弃 componentDidUpdate //推荐使用 getDerivedStateFromProps// 推荐使用componentDidUpdate(prevProps){if(prevProps.item.width!this.state.width){this.setState({width:…

斐波那契数列(Fibonacci)三种方法实现

1. 列表输出 def Fibonacci_li(num):a 0b 0li []for i in range(num):li.append(a)a, b b, a breturn liprint(fibonacci(10))# 输出结果:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 复制代码2. 递归输出 # 获取相应下标的数据 def Fibonacci(num):# 退出条件if num …

全局方法

js的本质就是处理数据,数据来自于后台的数据库,变量就起到了临时存储数据的作用。 数据类型: 1、基本数据类型(简单的赋值) a、字符串(string) b、数字(number) c、布尔(…