预测:函数式未来与数据库磨合,sql消失

13-01-04 SpeedVan
函数的逻辑性与关系数据库的是一致的,而逻辑在不同语言之间是自然贯穿的,也就是说若果所有都用函数来表达的,可以使sql消失,那么由于sql引发的问题自然就消失。

可以这么理解:数据库就是函数程序中的一个预先写好集合,那么我们每一个查询都会自然而然地变成高阶函数,因为sql本来就需要数据库本身解析成一个个函数来执行,去掉解析,函数直接调用函数。

在scala中使用mongodb就是这样的感觉,尽管实际上并不是这样。现在我没有发现这么“彻底的”数据库,但我估计,函数式真的冒头成功的话,这方向必定跑不了。

              

10
banq
2013-01-04 08:58
赞同这点,函数式FP特点是不变性,可变性状态都由数据库完成,而且数据库这里虽然取名数据库,实际上是带有内存状态的数据管理系统,其实也就是领域模型的代名词。

使用领域模型表达状态更尊重复杂的业务,使用数据库直接表达状态都是因为业务结构比较简单,都是非结构数据,比如Twitter的微博其实很简单,类似EMail是一种非结构数据,因此没有必要使用领域模型方式,直接使用NoSQL即可。

如果我们从函数FP+数据库这个层次再提升,从不变性+可变性分离这个角度来看,就更加宏观,CQRS架构也可以包括进来,在CQRS中Event一定是被设计为不可变的值对象,这样保证事件在传导激活围绕可变状态的行为之前,一直保持不可变性,这对于CPU执行相当给力。

为什么函数语言Scala最终是编译成Java的JVM可执行语言?其实说明Java的基因中是有不变性和可变性分离的,这个基因要得到显式体现,除了使用Scala语言,还可以通过CQRS+Event Sourcing来实现。

以上只是个人想法。

[该贴被banq于2013-01-04 09:28修改过]

SpeedVan
2013-01-04 18:23
2013-01-04 08:58 "@banq"的内容
赞同这点,函数式FP特点是不变性,可变性状态都由数据库完成,而且数据库这里虽然取名数据库,实际上是带有内存状态的数据管理系统,其实也就是领域模型的代名词。

使用领域模型表达状态更尊重复杂的业务,使用数据库直接表达状态都是因为业务结构比较简 ...

其实sql如何继续发展,到达我们平常使用的语言类似的程度,那么它可以发扬光大。不过当时关系数据库跟随的是指令式,加上又是表思维,所以很多都越走越畸形。我觉得NoSQL是回到正道,而不是进步。最简单:为什么要有表?

关于可变不可变,我觉得Haskell的monad是一种很好的分离手段,大家都知道这是范畴学应用。其潜力在CQRS+Event Sourcing之上。

其实CQRS和Event Sourcing正在做着跟函数一样的事情。

tangxuehua
2013-01-04 22:13
我现在也在尝试使用CQRS+Event Sourcing来开发应用,并且自己也做了一个框架。但实际真正做项目又是另一回事了,因为实际项目往往会遇到各种问题,之前在“只保存最终状态下很容易解决的问题”,在event sourcing下,都会变成大问题。所以,这更加要求我们对DDD,event sourcing要有很深厚的理论理解和实践经验,否则真的项目很可能会失败。

Speedvan提到"CQRS+Event Sourcing正在和函数式编程做着同样的事情",我现在体会最深刻的是:DDD中的值对象让我现在非常喜欢他。我现在尽量要做到一次逻辑计算,没有中间的不一致性,只有各种临时的值对象,状态更改没有过程,只有一次性的替换;这种无并发副作用的好处非常爽。就像函数式编程是无副作用的一样。以前我们要改一个对象的状态,一般也没问题,因为有数据库事务给我们保存修改的原子性。但其实之前我们都忽略了值对象对于对象状态修改的重大意义,在in-memory时代的编程,值对象的意义才更加突出吧,否则在多线程并发的情况下估计内存中导出是状态不一致的对象。

关于event sourcing,我目前还只是看到了它最初被我所认识的特性吧,1)就是用事件来表示对象,而不是只存储对象的最后当前状态,这点说起来容易,但要做到,估计还得依赖于稳定的框架作为支撑。2)另外,基于这个特点,重建对象也可以通过另一个思路,就是通过事件溯源,但这是有前提的,就是事件的业务含义不能改变,如果要改变,也必须与之前的含义兼容,否则事件溯源将变得没有意义。因为我们存储的事件仅仅是“发生的事情”,但是对这个事情的响应逻辑(实际根据事件信息改变聚合根状态的逻辑)是在我们的代码里,即所谓的聚合根内部的事件响应函数。该响应函数的逻辑是不能改变的。要做到事件的响应逻辑不会改变,就必须做到事件的含义不能改变,如果事件含义改变,就必须定义新的事件。

以上这两点我觉得就足以另大部分人无法使用event sourcing了。

tangxuehua
2013-01-04 22:37
兴致来了,再多说一点吧,呵呵。

就是,在event sourcing模式下,我在聚合根基类里设计了三个方法,分别是:

1. OnEventHappened(event) //表示某个事件发生了

2. WakeupAggregateRoot<T>(id) //表示要唤醒另外一个指定ID和类型的聚合根

3. OnAggregateRootCreated(aggregateRoot) //表示某个聚合根被创建了

有了这三个方法,在领域模型中,应用event sourcing模式实现聚合根与聚合根的交互时,就变得很游刃有余。

第一个方法就是通知有一个新的领域事件发生了,这个方法在所有的event sourcing框架中都有,老外一般命名为ApplyEvent(个人觉得这个名字不怎么贴切);这里我们的目的应该是通知某个事件发生了,那么按照惯例On是通知什么什么的意思,Happened表示发生了,合在一起正好明确的表示某个事件发生了。针对该事件,聚合根可以首先自己响应,然后改变自己状态;而外部系统,如CQRS的查询端,则可以event bus发布事件后收到该事件,然后响应它。

第二个方法是在“我们如果想设计通过ID来关联聚合根时”,就变得非常有用,因为我们只有另外一个聚合根的ID,但有时我们又想访问该聚合根的某个方法,在经典的DDD下,一般只有两个方法,要么通过第三方领域服务来协调,要么直接通过对象引用关联,利用ORM的lazyload机制。而在event sourcing模式下,聚合根是通过事件溯源得到,所以无法应用ORM来实现,自然也就无法利用lazyload的机制,所以我设计了该方法(相当于用自己的方式实现了lazyload)。为了思考该方法的名称,我想了很久,最终还是用WakeupAggregateRoot,因为我认为领域模型中的所有对象都是生活在内存中的对象,不存在从另外一个地方取过来的说法,对于聚合根来说,仓储对他是不可见的,仓储可以理解为存放了整个领域模型中所有聚合根对象的一个内存池。我们一定是在需要该关联对象的时候直接“呼叫它”,”唤醒它“,但是我们又不想直接存放该对象的引用,所以需要一种手段明确将它从内存唤醒,从而与他配合工作;

第三个方法的背景是:有时我们在某个聚合根内做了一件事情,并且由于职责所需它在做完这件事情后随即创建了另外一个聚合根对象。但是new 一个对象虽说也是创建对象,但是终究没有其他任何人知道你new了一个对象,当然,我们都知道聚合根对象只是被new出来还是不够的,我们终究还是要将它放入某个内存集合或数据库。但是new出该聚合根对象的对象又不知道该怎么把该对象放入集合或数据库,它能做的只能是通知别人“我new了一个聚合根”,我觉得:"聚合根不应该有意图去告诉别人请你帮我保存一下另一个聚合根或保存一下自己,对一个聚合根来说,它应该压根就不知道保存为何物,保存一般来说是以前数据库的思维"。所以,我引入了OnAggregateRootCreated方法,用来起到某种通知作用,这种通知可以这样理解:"我new了某个聚合根,请大家知晓一下“,仅此而已,完全没有更多如”请大家帮我保存一下“的意图。

我发现,这三个方法的引入,可以解决大部分event sourcing模式下聚合根与聚合根的交互问题,从此我基本不用再依赖于领域服务之类的设计构造块了。当然DCI中的角色与场景的引入,那对event sourcing以及DDD来说,又可以更自然的进一步的消除领域服务了。

[该贴被tangxuehua于2013-01-04 22:55修改过]

猜你喜欢
2Go 1 2 下一页