返回

文章详情

Biff.core:Clojure Web 应用的系统组成

Hacker News2026年6月9日 16:12

正如我之前所写的,我一直在努力将 Biff 拆分为多个单独的库,并在这个过程中改变各种东西。我已完成所有十二个库的粗略草稿,现在正逐个审阅它们进行打磨和发布。第一个库现在准备好了,biff.core:Biff 项目的系统组成和其他接口。这是将所有其他库联系在一起的粘合剂,这就是我首先发布它的原因。很长一段时间以来,Biff 拥有这种“模块和组件”的结构,其中每个应用程序命名空间在您的项目中公开一个“模块”映射,然后你有一堆样板代码将这些模块中的内容组合成一个单一的“系统”映射,接着我们在启动时通过您的“组件”函数传递系统映射。Biff 2 保留了这种结构,并增加了一些东西来处理这些样板代码。为了举例说明我所说的内容,请参见这段代码,它从您的模块中提取 :routes(和 :api-routes)键并将其转换为系统映射的 :biff/handler 值。我希望有一种一流的方式能够将这种逻辑干净地提取到一个库中,这样库的指令就可以是“将这个模块添加到您的项目中”,而无需附带“然后将所有这些内容粘贴到您的主命名空间中”。因此,这个新的 biff.core 库包括一个“初始化函数”的概念。这些是接受模块集合并返回可以合并到您系统映射中的单一映射的函数。太好了。这里有一个示例。初始化函数存储在您的模块映射中的 :biff.core/init 键中,因此我们得到了美好的“您只需模块(好吧,还有组件)”效果。这里的主要复杂性在于,定义 (def handler ...) 变量的样板代码实际上有一个好的附带好处:晚绑定。如果您更改任何模块,处理程序变量将会更新,如果您将 :biff/handler 在您的系统映射中设置为变量而不是值(#'handler),传入的 Ring 请求将会获得最新的处理程序,而您无需重启 Web 服务器。如果我们将这个样板提取到库代码中,我们就得不到变量。最终我采用了这个解决方案:初始化函数接受模块向量的变量,而不是向量值本身。系统映射中任何您希望在不重启情况下更新的东西都需要是一个函数。在某些情况下,这意味着您需要在系统映射上设置一个 :com.example/get-my-thing 函数而不是设置 :com.example/my-thing 键,该函数返回 my-thing。系统映射上的该函数应该解引用模块变量并将其传递给构建您所需的任何东西的记忆函数(例如 Ring 处理程序)。再次查看这个示例。结果在美学上令人愉悦:您得到了一个干净的主命名空间,基本上不需要太多更改,您所做的只是添加模块和组件。总是有进一步合并事物的诱惑。为什么要有单独的组件向量?为什么不让模块支持 :biff.core/on-start 和 :biff.core/on-stop 键,然后以某种方式表达这些生命周期函数之间的依赖关系,以便我们可以按正确的顺序调用它们?答案是这样我们就不必有某种方式来表达这些生命周期函数之间的依赖关系,以便我们可以按正确的顺序调用它们。自己将组件放在正确的顺序并不难(尤其是因为 Biff 启动项目为您完成这项工作),这样更容易理解组件的工作原理。这只是一系列函数,您可以通过它传递一个映射。如果您正在处理一个有很多状态资源的项目,以至于很难跟踪所有资源,您总可以在 Biff.core 之前在其上层构建一些东西,弄清楚您的组件向量应该是什么。广告:我的团队正在招聘一名高级软件工程师,主要编写 ClojureScript 和 Python。我们为可再生能源项目开发建模软件。

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡