Functional programming 函数式编程中数据库句柄和初始化的处理

Functional programming 函数式编程中数据库句柄和初始化的处理,functional-programming,Functional Programming,我有几个处理数据库交互的函数。(如readModelById、updateModel、findModels等),我尝试以函数式风格使用 在OOP中,我将创建一个类,该类在构造函数中接受DB连接参数,创建数据库连接并在实例中保存DB句柄。然后,这些函数将只使用“this”中的DB句柄 FP中处理这一问题的最佳方法是什么?我不想在整个应用程序中使用DB句柄。我曾考虑过在函数上部分应用程序来“烘焙”句柄,但这会创建难看的样板代码,一个接一个地执行并将其返回 在FP中,这样的事情的最佳实践/设计模式是什

我有几个处理数据库交互的函数。(如readModelById、updateModel、findModels等),我尝试以函数式风格使用

在OOP中,我将创建一个类,该类在构造函数中接受DB连接参数,创建数据库连接并在实例中保存DB句柄。然后,这些函数将只使用“this”中的DB句柄

FP中处理这一问题的最佳方法是什么?我不想在整个应用程序中使用DB句柄。我曾考虑过在函数上部分应用程序来“烘焙”句柄,但这会创建难看的样板代码,一个接一个地执行并将其返回


在FP中,这样的事情的最佳实践/设计模式是什么?

数据库操作实际上不是函数式编程的目标领域,因为它们是副作用。FP的主要价值在于尽可能使用纯函数

然而,OOP中也存在类似的情况,这可能表明正确的方法是将数据库资源作为参数。考虑在OOP中使用坚实的原则实现DB。因此,每个DB方法都有一个接口,每个接口至少有一个实现类

/C#
公共界面iGetRegistration
{
公共任务GetRegistrations(日期时间日);
}
公共类GetRegistrationsImpl:IGetRegistrations
{
公共任务GetRegistrations(日期时间日)
{
...
}
私有只读数据库资源_db;
公共GetRegistrationsImpl(数据库资源数据库)
{
_db=db;
}
}
然后,为了执行用例,您只传递所需的依赖项,而不是整个DB操作集。(假设
isaverRegistration
存在并且定义如上所述)

/C#
公共异步任务寄存器(
IGE注册a,
我的注册号是b,
请求注册请求
)
{
var注册=等待a.GetRegistrations(请求的日期);
//检查现有注册并确定请求是否有效
//如果不是,则抛出异常?
...
返回等待b.保存注册(…);
}
在调用此代码的地方,您必须更新这些接口的实现,并为它们提供
DbResource

var a=新的GetRegistrationsImpl(db);
var b=新的SaveRegistrationImpl(db);
...
返回等待登记簿(a、b、请求);
注意:您可以在这里使用DI框架来避免一些样板文件。但我发现这是向彼得借钱给保罗。学习DI框架以及如何使其正常运行所付出的代价与自己连接依赖项所付出的代价相同。这是新团队成员必须学习的另一项技术

在FP中,您可以通过简单地定义一个以DB资源为参数的函数来做同样的事情。您可以直接传递函数,而不必将它们封装在实现接口的类中

//F#
let getRegistrations(数据库资源:db)(日期:日期时间)=
...
让我们保存注册(DbResource:db)=
...
用例功能:

//F#
让我们注册fGet fSave请求=
异步的{
let!registrations=fGet request.Date
//在这里调用您的业务逻辑
...
做!保存。。。
}
然后你可以这样称呼它:

注册(getRegistrations db)(saveRegistration db)请求
这里的
db
的部分应用类似于构造函数注入。与不必为每个DB操作定义接口+实现相比,将其传递给多个函数的“损失”是最小的


尽管是功能性的第一语言,但上述原则上与OO/SOLID方式相同。。。只需要更少的代码行。要进一步进入功能领域,您必须努力消除业务逻辑中的副作用。副作用可能包括:当前时间、随机、引发异常、数据库操作、HTTP API调用等

因为F#不要求你申报副作用,我指定了一个应该停止使用副作用的地方。对我来说,用例级别(
上面的寄存器
函数)是副作用的最后一个位置。任何低于此的业务逻辑,我都致力于将它们推向用例。这是一个学习的过程,所以如果一开始看起来不可能,不要气馁。现在就做你必须做的,边走边学

我试图对计划生育的好处以及如何实现这些好处设定正确的期望