Oop 在事件源系统中组合业务逻辑?
据我所知,在事件源系统中执行命令的工作原理如下(简化):Oop 在事件源系统中组合业务逻辑?,oop,functional-programming,domain-driven-design,event-sourcing,Oop,Functional Programming,Domain Driven Design,Event Sourcing,据我所知,在事件源系统中执行命令的工作原理如下(简化): 验证命令DTO本身。必要时拒绝 通过按顺序应用事件(投影),加载/水合受影响的骨料 通过将命令与聚合状态交叉检查,验证是否可以从业务角度应用该命令。必要时拒绝 对聚合执行业务逻辑。聚合本身保持不变,因此使用它调用的方法只输出记录所发生事件的事件。 以正确的顺序原子地持久化生成的事件 返回给客户 我对执行业务逻辑的部分感兴趣。在非事件源系统中,这非常简单:调用方法以保持其不变量的方式更改聚合的状态。我们可以轻松地链接和组合方法调用,并轻松地
- 在进行任何更改之前,先创建聚合的深层克隆,然后对其进行常规变异,最后通过将修改后的实例与其克隆进行比较来生成“diff事件”。缺点:生成的事件级别非常低,并且非常接近当前的数据库模式,这使模式演化复杂化
- 除了发出事件之外,还像往常一样修改聚合的状态。缺点:现在我复制了投影逻辑。谁能保证以后应用生成的事件与生成它们的方法实现具有相同的效果
- 在调用另一个方法之前,使用投影逻辑动态(在内存中)应用生成的事件,所有这些事件仍然位于同一处理程序和事务中。缺点:这会增加业务代码的基础架构问题。
- 更新:如答案中所述,使用自定义函数组合实现,可以从用户代码中隐藏/抽象特殊投影。这样,每个命令将获取一个聚合并发出一组事件,在调用链中的下一个命令之前,组合逻辑将使用投影逻辑将这些事件应用于聚合。有关一些理论背景,请参见自由单子
那么,在事件源系统中,业务逻辑组合通常是如何完成的呢?我不会说,在命令处理程序中应用投影逻辑会使业务代码因基础设施问题而膨胀。对于事件源聚合,核心投影逻辑(Scala表示法中类型为
(a,event)=>a
,即接受聚合和事件并返回新聚合的函数)是聚合的基本部分,而不是基础结构问题
因此,如果这样一个聚合有一个方法(部分Java表示法)一个applyEvent(Event e)
,并且命令处理程序正在累积一系列事件,那么折叠序列以获得一个新状态(复制投影操作)对我来说一点也不夸张(不可否认,事件源在某种意义上是将函数式编程潜入企业开发(人们对免费monad有很多机械的同情),因此在一种没有“lambda演算性质”的语言中这样做可能会很痛苦)
撇开这一点不谈,您会注意到差异事件的级别非常低。事件源倾向于支持高级别事件:为命令发出“很多”事件可能是一种迹象,表明事件希望更高级别地描述发生了什么(以及为什么)更改。可以使用几乎相同的方式处理事件(例如,对于留言板,UserDeletedPost
事件和mediatordeletedpost
事件可能都会被Post
聚合以相同的方式解释,但事件之间的语义差异相当大)并且在命令处理程序中进行一些重复检查。事件源系统中具有简单聚合的趋势也支持这种方法
在某些情况下,一个特别粗粒度的命令可以分解为多个命令(实际上是一个传奇)。如果聚合将支持此类粗粒度命令,则可能需要对其进行修改,以支持粗粒度命令的原子性错觉。映射到参与者模型的事件源实现有时可以简化这一点。在我们的框架中,我们最终得到一个命令-一个事件方案。是的,这是一个限制,但这似乎是在并发分布式环境中确保一致性的唯一方法。在这种情况下,多事件逻辑由sagas执行
我们可能允许写入多个事件,但是,正如您正确提到的,您无法评估这些事件之间的状态,并且您不能/不应该逐个写入它们,因为其他一些命令可以在这两个事件之间更改聚合状态。详细阐述了一元解释(在Scala中)在Debashish Ghosh的功能域和反应域建模中。命令在事件上成为自由单体(因此命令可以通过单体绑定进行组合),事件解释器基本上是一个状态单体。合成特定命令的saga方法基本上是去单体化