如何设置非平凡的数据持久性?

时间:2015-06-19 03:07:50

标签: f# functional-programming

最常见的问题是:如何以一种很好的功能方式设置非平凡的数据持久性?
我更喜欢那些有实际经验的人,实际上已经做过的人的答案。但我也很高兴听到有关这个问题的任何其他想法。

现在,让我通过一些例子澄清我的意思。

假设我有一些我的程序处理并需要保留的数据。说,我们的老朋友Employee

module Employees =
  type Employee = { Name: string; Age: int; /* etc. */ }

module EmployeesPersistence =
  type EmployeeId = ...

  let getEmployee: (id:EmployeeId -> Employee) = ...
  let updateEmployee: (id:EmployeeId -> c:Employee -> unit) = ...
  let newEmployee: (c:Employee -> EmployeeId) = ...

实现持久性函数的方式并不重要,假设它们是关系数据库,基于文档的数据库,甚至是磁盘上的文件。我现在不在乎。

然后我有一个程序可以帮助他们:

module SomeLogic =
   let printEmployees emps = 
     let print { Name = name, Age = age } = printfn "%s %d" name age
     Seq.iter print emps

到目前为止如此直截了当。

现在,假设我有另一种数据Department,它与Employee有关,但需要独立存储(在单独的表中,单独的集合,单独的文件中)。登记/> 为何独立?说实话,我真的不知道。我知道的是它需要有自己的身份,以便我可以显示/更新它而不需要任何特定的员工。由此,我推断存储必须是分开的,但我对其他方法持开放态度。

所以我可以做与Employees一样的事情:

module Departments =
  type Department = { Name: string; /* etc. */ }

module DepartmentsPersistence =
  type DepartmentId = ...
  let getDepartment, updateDepartment, newDepartment = ...

但是现在,我该如何表达这种关系呢?

尝试#1:忽略持久性。

在“抽象所有事物”这个历史悠久的传统中,让我们设计我们的数据模型,同时假装没有持久性。我们稍后会添加它。有一天。它是“正交的”。这是“辅助”。或者其他一些。

module Employees =
  type Employee = { Name: string; Age: int; Department: Department }

module SomeLogic =
   let printEmployees emps = 
     let print { Name = name, Age = age, Department = { Name = dept } } = printfn "%s %d (%s)" name age dept
     Seq.iter print emps

这种方法意味着每次我从持久存储加载员工时,我也必须加载部门。浪费的。然后,Employee - Department只是一种关系,但在一个真实的应用程序中,我有更多的东西。那么,我每次都要加载整个图表呢?非常昂贵。

尝试#2:拥抱持久性。

好的,因为我不能在Department中直接包含Employee,所以我会包含部门的人工身份,因此我可以在需要时加载它。

module Employees =
  type Employee = { Name: string; Age: int; Department: DepartmentId }

现在我的模型本身没有任何意义:我现在基本上建模我的存储,而不是我的域 然后,事实证明需要部门的“逻辑”函数必须通过“loadDepartment”函数进行参数化:

module SomeLogic =
   let printEmployees loadDept emps = 
     let print { Name = name, Age = age, Department = deptId } = 
       let { Name = deptName } = loadDept deptId
       printfn "%s %d (%s)" name age deptName
     Seq.iter print emps

所以我现在的的功能本身没有意义。持久性已成为我的计划中不可或缺的一部分,远非“正交概念”。

尝试#3:隐藏持久性。

好的,所以我不能直接包括部门,我也不喜欢包括它的ID,我该怎么办? 这是一个想法:我可以包含部门的承诺

module Employees =
  type Employee = { Name: string; Age: int; Department: () -> Department }

module SomeLogic =
   let printEmployees emps = 
     let print { Name = name, Age = age, Department = dept } = printfn "%s %d (%s)" name age (dept()).Name
     Seq.iter print emps

到目前为止,这看起来是最干净的方式(顺便说一句,这就是ORM所做的),但仍然没有问题。
首先,该模型本身并不合理:一些组件直接包含在内,另一些组件通过“promise”包含,没有任何明显的原因(换句话说,持久性通过泄漏)。登记/> 另一方面,将部门作为承诺可以阅读,但我该如何更新?我想我可以使用镜头代替承诺,但后来变得更加复杂。

所以

人们如何实际做到这一点?我应该在哪里妥协,哪些有效,哪些无效?我是否真的必须妥协,是不是有“纯粹”的方式? 既然有明显真实的数据驱动应用程序,那么这些事情必须有某种方式完成,对吧?右?..

0 个答案:

没有答案