【问题标题】:Where to put database access/functionality in clojure application?在 clojure 应用程序中将数据库访问/功能放在哪里?
【发布时间】:2016-07-05 14:51:05
【问题描述】:

我正在编写一个小型 Clojure 应用程序,它与具有 2-3 个不同集合的 MongoDB 数据库进行大量交互。

我来自 OOP/Ruby/ActiveRecord 背景,其中标准做法是为每个数据模型创建一个类并授予每个类访问数据库的权限。我已经开始在我的 clojure 项目中做同样的事情。每个“数据模型”都有一个命名空间,每个命名空间都有自己的数据库连接和 CRUD 函数。但是,这感觉不是很实用或类似 clojure,我想知道是否有更惯用的方法,例如使用 datadatabase 命名空间和 get-post 之类的功能,并限制只能访问该命名空间的数据库。

这似乎有利于将数据库客户端依赖项隔离到一个命名空间,并将纯函数与具有副作用的函数分开。

另一方面,我将有一个命名空间,我需要从我的应用程序的许多不同部分引用它,而拥有一个名为“数据”的命名空间对我来说似乎很奇怪。

在 Clojure 中是否有一种传统的惯用方式?

【问题讨论】:

    标签: clojure functional-programming


    【解决方案1】:

    Stuart Sierra 的 Component 库提出了一种在 Clojure 应用程序中管理状态的好方法,可以说是最惯用的 (scored 'adopt' on the Clojure radar) 方法。简而言之,Component 的理念是将所有有状态资源存储在单个 system 映射中,该映射明确定义了它们的相互关系,然后以这样一种方式构建您的代码,即您的函数只是将状态传递给彼此.

    【讨论】:

      【解决方案2】:

      连接/环境访问

      您系统的一部分将是管理您的应用程序的“机器”:启动 Web 服务器、连接数据存储、检索配置等。将此部分放在与您的业务逻辑(您的业务逻辑命名空间不应该知道这个命名空间!)。正如@superkondukr 所说,Component 是一种经过实战检验且有据可查的方法。

      将数据库连接(以及与此相关的其他环境依赖项)传达给您的业务逻辑的推荐方法是通过函数参数,而不是全局变量。这将使一切都更可测试、对 REPL 友好,并且关于谁依赖谁更明确。

      因此,您的业务逻辑函数将接收连接作为参数并将其传递给其他函数。但这种联系首先来自哪里?我这样做的方式是在事件/请求进入系统时将其附加到它们。例如,当您启动 HTTP 服务器时,您会将连接附加到每个传入的 HTTP 请求。

      命名空间组织:

      在 OO 语言中,对数据的传统支持是表示数据库实体的类的实例;为了提供一个惯用的 OO 接口,业务逻辑被定义为这些类的方法。正如 Eric Normand 在最近的时事通讯中所说,您将模型的“名称”定义为类,将“动词”定义为方法。

      因为 Clojure 强调用于传递信息的普通数据结构,所以您实际上并没有这些动机。你可以仍然按实体组织你的命名空间来模仿这个,但我实际上并不认为这是最佳的。您还应该考虑这样一个事实,即 Clojure 命名空间与大多数 OO 语言中的类不同,它不允许循环引用。

      我的策略是:按用例组织命名空间

      例如,假设您的域模型有用户和帖子。您可能有一个用于用户 CRUD 和核心业务逻辑的 myapp.user 命名空间;同样,您可能有一个 myapp.post 命名空间。也许在您的应用程序中,用户可以喜欢帖子,在这种情况下,您将在需要myapp.usermyapp.postsmyapp.like 命名空间中管理它。也许您的用户可以成为您应用中的朋友,您将在 myapp.friendship 命名空间中对其进行管理。也许您有一个小型后台应用程序,其中包含所有这些数据可视化:例如,您可以将其放在 myapp.aggregations 命名空间中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-09-06
        • 2013-11-14
        • 2010-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多