本文共 5254 字,大约阅读时间需要 17 分钟。
Joakim Tendstrand是Clojure和Datomic的架构师和开发人员,最近向作者强烈推荐了自己团队的产品,这个名字结合了“很多”和“石头”的概念。它是一种软件体系结构,利用许多构件来组合系统,所有构件就像工作在一个地方一样。Polylith克服了单体、微服务、无服务架构的一些缺点,本文希望从概念和实现上对这个新架构做一个介绍,以引起开发使用者的兴趣可以去采纳并应用它。
\\Polylith的特点是功能简单和可组合性。它可以帮助我们构建简单、可维护、可测试和可扩展的系统。它像乐高(Lego™)一样利用具有共享函数属性的构件块来构建系统。
\\ \\将构件组合成系统:
\\ \\\\下面对比了开发和生产体验中的三个主流软件架构:
\\开发体验
\\ \\生产体验
\\这些体系结构提供了大量如何部署系统的指导,但对于如何在每个系统中构造代码指导很少。
\\为了帮助解释这个架构,Joakim和他的团队发明了一种隐喻(metaphor),一个基于Clojure的体系结构,还演化了一个开发工具。
\\ \\完整的Polylith体验
\\隐喻有助于理解Polylith的建筑概念。Polylith系统由三种类型的构件组成:组件(Component)、基底(Base)和库(Library)。每种类型的构件都表示打包代码的特定方法,其目标是使它们简单地组合成系统。
\\隐喻建立在两个基本的功能概念上:函数和命名空间。
\\函数具有许多属性,封装性、简单性、无状态、纯度。这些属性使函数(尤其是纯函数)具有牢固的、可组合和可测试的代码单元。这就是为什么选择在隐喻中使用类似乐高的砖块,因为乐高就是一个牢固的、可组合的积木。
\\ \\“乘法”乘以其自变量并返回结果
\\ \\绿色的部位表示函数的实现
\\ \\红色的部位表示对另一个函数的依赖关系
\\ \\底部的方形孔代表函数的签名
\\签名: 函数的名称、参数和返回结果。
\\让我们来看一个更大的函数,以及函数调用在隐喻中的作用。
\\ \\名称空间打包了相关功能,作为一个共同术语并被赋予一个名称。在Java中,这个概念被称为包,在其它语言中,它被称为模块。让我们看看如何将函数打包到Clojure中的命名空间中:
\\ \\组件是Polylith的核心构件,将系统划分为封装的和可组合的代码块。通过将它们的实现与它们的接口分离来实现它们的封装和可组合性。
\\ \\浅绿色层是接口,深绿色层是实现
\\设计良好的组件具有单一职责、描述性名称,并且公开了一组仅与其职责相关的清晰功能。举几个例子,一个组件可以:
\\基底是Polylith的基础构件,是一个系统与外界沟通的门户。基底通过将实现与接口分离来实现封装。
\\ \\浅蓝色层是接口(API),深蓝色层是实现
\\每一个基底都有一个责任:向外界展示一个系统的功能。在Polylith中,可以在基底中自由地使用任何API技术,例如REST、gRPC、GraphQL或只是简单的旧的命令行。
\\基底和组件之间的区别是基座具有平的底部。这意味着基底是不可合成的,它们不能被其他构建块所依赖。基底总是位于Polylith的底部。
\\库(Library)是Polylith的“屋顶”构件,是别人的代码,内部代码依赖于这些代码。
\\ \\Polylith的构件组合方法使得构建系统既简单又有趣。一旦设计好了基底的API,那么只需要组成一组正确的组件和库,这些组件和库就能实现了我们公开的功能。让我们来看看一个完整的系统:
\\ \\系统:一个基底,两个组件,三个库
\\生态系统(ecosystem)是Polylith的扩展解决方案。它们是一个系统的集合,为外部世界提供一套统一的功能。在Polylith中,它们是通过一个“电缆”连接的,它从系统B中的一个组件的外部依赖性扩展到系统A的一个端点。
\\ \\系统B连接到系统A的API提供的端点
\\\\Polylith架构的设计主要围绕两个目标:简单性和速度。简单性来自于保持概念的独立性,速度来自于把我们所有的代码放在一个地方。这一节介绍Polylith是如何利用Clojure来实现这两个目标的,并用一个RealWorld的示例程序来描述(代码参考文章末链接)。
\\工作空间“workspace”是Polylith项目中的根文件夹,这是使用组件组装系统的地方。
\\可以把工作空间想象成一个办公桌:一个与我们的积木一起工作的地方。它用“抽屉”来保存构件,用“架子”来组装系统,还有一个“工作台”作为开发环境。
\\ \\基底“bases”文件夹是一个“抽屉”,它保存了工作空间中的所有基底。
\\ \\每个基底都有一个标准的Clojure项目结构:使用Polylith.clj文件(类似于pom.xml,被用于Maven和Java),一个readme.md文件,一个资源文件夹,一个src文件夹,和一个测试文件夹。每个基底都是一个项目,可以将它们编译为单个的工件,这保证了它们与所有其他构件解耦。
\\ \\“api”命名空间公开了所有的基底
\\组件“components”文件夹是另外一个“抽屉”,它保存了工作区中的所有组件。
\\ \\RealWorld组件“抽屉”包含八个组件
\\环境“environments”是存储所有环境的文件夹。每个环境都是一个可以在IDE或代码编辑器中打开的项目。我们可以将环境文件夹想象成一个“工作台”,它可以访问工作空间中的所有环境。
\\ \\环境是与构件一起工作的地方
\\这给了我们选择多个环境的条件,每个环境都有不同的组件和基底。
\\接口“interfaces”是存储所有“工作空间接口”的文件夹。“工作空间接口”保证构件的实现互不访问,实现组件之间的隔离。
\\ \\我们可以把系统“systems”文件夹想象成一组“架子”,它把工作区中的所有系统都保存起来。
\\ \\系统将选定的构件组装成整个工件,通过构建和部署多个工件,从单体开发(monolithic在一个环境中拥有所有构件)出发,实现水平可伸缩性的提升。
\\\\Polylith工具优化Polylith系统的产生、发展、测试和建造。Polylith工具帮助使用者:
\\学习Polylith工具的,了解它是如何工作的以及如何使用它。如果想快速浏览这个工具的所有特点,从开始是一个很好的选择。
\\\\让我们把Polylith与之前看的三种结构进行比较。Polylith系统可以作为单个工件(如单体)、作为生态系统中协作的多个工件(如微服务)部署,或者作为无服务器体系结构中的Lambda功能。
\\Polylith的单一开发环境允许我们在一个地方与所有的构建块一起工作。这将我们的开发经验与我们所选择的部署架构断开连接。
\\ \\优点 | 解释 |
调试 | Polylith开发环境中的所有代码都可以在单个中运行,提供了一流的REPL驱动的开发和调试体验。 |
快速反馈 | Polylith工具跟踪自上次成功测试或构建以来哪些基础和组件已经被更改,并且只进行编译和测试。这给我们在本地开发环境以及持续集成环境中构建和部署提供了闪电般的快速反馈。 |
重构 | 组件接口通过简单函数调用连接到它们的实现。这意味着可以用IDE/代码编辑器安全地重构接口。 |
可重用 | 组件是可重用的,因为它们是封装的,无状态且可组合的。它们可以在单个系统和多个系统中重复使用。 |
简洁性 | Polylith构件只是代码,是接口后面的函数集合。接口保证封装,这确保了代码库分离,保持更简单的系统。 |
易测性 | 组件的封装和功能性质使得它们作为完整的系统,易于被隔离测试 |
Polylith不仅允许延迟部署的决策,还允许我们在需要时轻松地改变部署的架构。这是因为Polylith可以将组件重组成任意数量的系统,这样就可以很容易地部署它们以满足性能需求。
\\ \\优点 | 解释 |
成本 | Polylith允许我们避免对解决方案的分布做出过早的设计决定。这使我们避免了“过早分发”,降低了部署复杂度并降低了运营成本。 |
部署 | Polylith工具使得构建和部署过程在本地和在我们的CI(持续集成)环境中都简单且无缝。 |
可伸缩性 | 当Polylith系统没有达到需要的性能/可伸缩性时,那么Polylith使得创建新系统和水平伸缩变得容易。可以重用每个新系统中的现有组件。 |
六个实际应用中的Polylith,所有服务的真实用户
\\这些项目的规模和复杂度,从一个完整的招聘平台到一个简单的Web应用程序。这些项目告诉我们,组件是一个强大的工具,可以完成复杂的、任何规模的系统。构建像乐高一样的系统比我们能想象的更快、更容易、更愉快!
\\从一个单体迁移到Polylith,微服务或无服务器架构是相对容易的。这是因为我们可以单独地将每个人工制品迁移到Polylith系统,而不需要改变我们的部署。
\\不要忘记检查系统编译、构建和所有测试在每个步骤之后通过。
\\一个有五个组成部分的系统
\\微服务是一个由许多小单体组成的体系结构。这意味着迁移到Polylith就像在每个服务上执行一样简单:
\\ \\无服务器是一个由许多Lambda函数组成的体系结构。这意味着迁移到Polylith就像在每个Lambda上执行一样简单:
\\ \\具有单个端点的Lambda函数
\\如果你想了解更多关于Polylith:
\\感谢对本文的审校。
转载地址:http://bqhga.baihongyu.com/