使用Writer Monad进行事件来源
在先前的文章( F#的Writer Monad , 如何登录Apache Spark,一种功能方法 )中,我们讨论了使用Writer monad作为汇总事件的方法的想法。 但是,我们使用的是一个简单的文本记录器,与我们可以在应用程序中使用的任何常规记录器只有很少的差异。
从这种角度来看Writer monad时,似乎为日志记录聚合付出了额外的努力,而实现细节可能会使新手难以理解。
在本文中,我们将介绍Writer monad作为事件聚合器,可以将其以更通用的方式用于不同的用例,这表明它具有更广泛的可用性。 我们还将使用C#来表明该概念不仅与所谓的纯函数语言紧密相关,而且可以在我们选择的任何环境中使用。
主要思想是提出一种使用事件源概念以及Writer monad作为支持此工作的主要数据结构来聚合事件的方法。
活动采购
事件源是一种使用不可变结构将传入事件记录到系统中的方法,以便我们可以跟踪影响系统状态的事件。 这些事件应该在任何时间都可以重播,并且连续的事件流应收敛于可以通过按时间序列方式重播记录的事件而可以恢复或复制的系统状态。
为什么选择作家莫纳德?
Writer monad具有上述相同的特征,它可以记录使当前系统状态发生更改的一般事件。 同时,事件记录在不可变的结构中,而当前状态仅通过应用新事件而改变。
计算器日志
让我们从一个简单的例子开始,一个计算器实现。
我们的计算器可以执行一些操作,但是可以使用Writer monad跟踪其执行的操作。 这个初始示例类似于我们先前文章中显示的示例。
注意,我们的计算器是基于称为Writer
的结构定义的,我们将在下面定义。 主要思想是每个操作仅知道如何使用其执行的操作创建Writer
。
Writer monad可以定义如下。
在这里,我们定义以下操作。
- 通过构造函数
Bind
允许我们创建一个新的Writer
。 -
Map
允许我们更改当前状态。 -
FlatMap
在记录状态如何发生的同时更改当前状态。 -
Unsafe
检索当前状态和事件日志。
注意,更改状态的唯一方法是通过.Map
和.FlatMap
。
使用此结构,我们可以按以下方式使用计算器。
通过使用.UnSafe()
我们可以获得当前状态和事件日志。
在这种情况下,我们仅将Writer monad用作字符串日志,也许正因为如此,到目前为止,这看起来并不那么有趣。 但是,这些是我们接下来的示例的基础。
总和
此示例说明了如何使用Writer monad记录系统中发生的一系列整数事件,同时保持流处理器接收的值的总和。
首先定义源代码。
如我们所见,我们将使用无限/无限的随机整数流 。
现在,让我们看看如何使用Writer monad接收和处理这些事件。
当事件是不可变的时,这是一个非常清楚的事件源示例,通过重播它们,我们可以获得完全相同的最终状态,在这种情况下,该状态是所接收值的总和。
银行账户用例
最后一个示例将展示如何使用Writer monad处理发送到银行帐户的事件。 银行帐户支持两种基本操作,向其中添加资金并从中提取资金。
现在,假设我们有一个事件生成器,该事件生成器生成将由Writer monad作为流处理器处理的事务。
事务由类型Extraction
和Deposit
,这两个是我们将要处理的事件类型。
现在我们有了交易源,我们将从初始化初始状态开始。
然后,我们处理了许多事件,在这种情况下,我们对其中的100个事件感兴趣,但是实际上,它可以是任意数目。
请注意,每笔交易我们在银行帐户通过执行相应的操作.FlatMap
上accountState
。
最后,我们能够检索帐户的当前(最终)状态和已处理的事件。
有趣的部分是,当我们从相同的初始状态开始,然后在Writer monad日志上将相同的事务应用于初始状态时,我们应该以相同的最终状态结束。 值的状态更改是日志中记录的操作的直接结果。
使用纯功能方法
对于那些希望使用纯函数方法并避免变量accountState
发生突变的用户,我们可以向IEnumerable<T>
添加.FoldLeft
方法。 让我们看看如何。
首先,我们添加一个扩展方法,以便可以在C#中执行.FoldLeft
。
然后,我们只需要更改事件处理方式即可。
注意,通过这种方式,我们消除了Writer monad上的突变,而是使用.FoldLeft
和.FlatMap
构建新的.FlatMap
。
结论
Writer monad提供了一种使用不可变日志来跟踪更改的功能方法,该日志可以在任何编程语言(包括C#)中使用 。
同样,使用.FlatMap
允许我们以流畅的方式链接操作,从而提供声明性的流量控制,从而促进不变性。
有时,Writer monad会被误解,并且仅与应用程序日志有关,后者是由副作用库控制的空间。 但是,Writer monad不仅仅是记录器,它还是一个事件源,可用于以一种简洁优雅的方式记录状态变化。
我们还展示了C#如何支持这种方法,证明了Monads不仅仅限于纯函数式语言。
From: https://hackernoon.com/event-sourcing-using-writer-monad-b26a390285a