系统记忆模式:事件溯源的力量,上下文为王! – thenewstack


体现Gof设计模式之忘录记忆模式的设计不只是事件溯源,还有Git和区块链,分布式账本就是一种记录记忆模式,通过备忘录记忆获得上下文。
关于领域驱动设计(DDD)、命令查询责任隔离(CQRS)和事件溯源(ES)的书籍,文章,演讲,博客,视频很多。这三个概念相互补充,因此涵盖其中一个的几乎所有资源都至少会提及另外两个。本文也不例外,但它会尝试以不同的方式处理问题。代替图和简化的代码示例,它将为您提供一些类比并帮助您构建一些有用的思维模型,以更好地理解上述概念(尤其是ES)的适用性。
好的电影参考开始怎么样?以心理惊悚片“ Memento”为例,其中一个男人努力寻找妻子的凶手。棘手的是,他的病情很重-他无法建立新的记忆。实际上,情况并非完全是虚构的。早在1953年,亨利·莫莱森(Henry Molaison)由于进行脑外科手术而无法形成新的记忆,而脑外科手术本来可以帮助控制他的癫痫病。
想象一下,您知道一切对您正常工作至关重要的东西,但对过去几天,几周,几个月,几年没有记忆(包括书面的记忆)。考虑一下它将如何影响您执行每天应执行的任务的能力。无法形成新的记忆,一个人必须完全依靠记忆。
这意味着要根据自己的实际状态和目前为止周围环境的当前状态来做出决策。
 
软件系统应该有记忆吗?
似乎很多应用程序只需要知道当前状态即可正常运行。这就是它们的设计方式。以一个简单的产品目录系统为例。告知您现在有哪些产品可用,并向您显示有关它们的所有详细信息,这做得非常好。它不关心产品如何进入目录或如何以及何时进行修改(除非有审计要求)。
像上述产品目录这样的系统无法形成新的记忆。直到有人问:上周有哪些产品可用?无论您如何修改系统,如果没有记忆,它将无法回答该问题。
可悲的是,当您意识到需要记忆时,拥有记忆已经为时已晚。
实际上大多数软件系统确实都有一些记忆。我们通常称它们为日志。它们实质上是存储在文件,数据库,专用工具等之中。
它们包含有关过去在系统中发生的事情的信息,这些事情以某种方式或另一种方式是重要的。很难想象没有某种日志系统的现代软件系统。日志记录通常对开发人员来说非常无聊,对于维护人员而言,这是额外的工作,通常会被利益相关者所忽视,但是当事情出错时,它变得无价之宝。
当然,并非所有软件系统都是以这种方式设计的。您可能会惊讶地发现,关系数据库(某种程度上代表了状态一致性的系统的标志性代表)实际上很好地利用了他们自己的记忆。大多数数据库维护一个事务日志,该日志包含导致任何类型的状态修改的每个过去的事件。客户端与数据库交互时根本不使用该事务日志。但是,在数据复制和数据还原方案中,它是无价的。它允许数据库节点从任何以前的备份中还原,并通过重新应用备份后发生的事务日志中的所有事件来建立当前状态。
 
状态与记忆的关系
现在让我们将该概念扩展到最初看起来像另一个极端的情况。如何而不是不断更新一个状态,而是在每次需要时使用系统的内存(事务日志)从头开始(重新)构建它?
显然,对于诸如关系数据库之类的系统而言,这并不是一个非常明智的举动。处理大量事务并在内存中处理TB级数据不仅是一个糟糕的主意。关系数据库具有定义明确,已知的前期和某种通用功能范围的事实。他们所持有的数据的一致状态是他们的主要关注点,并且他们对这种状态如何形成的记忆很难以任何重要的方式丰富正常的操作。
但是其他系统呢?像以业务为中心的应用程序一样,我们许多人都花费宝贵的时间来构建。我们已经了解了记忆如何增强决策和任务完成能力。那些业务应用程序通常就是这样做的:做出决策并完成任务。赋予他们形成记忆的能力是很有意义的,不是吗?但是,然后,他们还需要保持状态吗?也许不是,因为他们可以根据需要从自己的记忆中确定自己的状态。
如果您觉得这个主意可笑,请不要放弃。可以肯定的是,大多数人最近很高兴地使用它,从前也认为它很可笑。
相反,请尝试回答以下问题:“现在您的钱包里有多少钱?” 如果您像大多数人一样,就不太可能想到这个数字。但是您可能还记得,在某个令人难忘的时间点,您拥有X,然后在另一个令人难忘的时间点,您获得/花费了Y,甚至在您不知道它之前,您就通过一系列事件构造了钱包的状态。
一点也不难,不是吗?请注意,您如何自动并下意识地忽略了在同一时间范围内收集的但与钱包上下文无关的一堆记忆。恭喜,您刚刚发现自己的想法是Event Sourcing。(有了记忆才有了上下文)
 
上下文是王
尝试告诉您的团队(不首先解释事件来源)有关您计划从过去的事件中不断(重新)构建状态的计划,他们肯定会担心您的心理精神问题。那是因为他们可能会考虑整个状态。随需应变的重构状态与将整个数据库加载到内存中的想法一样令人恐惧。
实际上,与数据库不同,大多数业务应用程序不需要整个状态来执行任务。实际上,他们通常只需要其中的一小部分。通常,状态的这一部分可以通过少数事件来(重新)构造。就像您的大脑里钱包的状态一样。这就是“聚合”(通常是域驱动设计)概念派上用场的地方。
根据定义,聚合它是一个构造块,将各个领域对象分组在一起并封装在一起,对于给定上下文中的状态修改,必须将这些对象视为一个整体。这给您带来了明确的界限。您只需(重新)生成聚合的状态即可执行所需的操作。在这种情况下,系统其余部分的状态无关紧要。
不需要在每个查询上从头开始重新生成整个系统的状态,对于大多数查询目的而言,包含某个人可能感兴趣的数据的状态投影(或您希望使用的子集)就足够了。这就需要“命令查询责任隔离CQRS”体系结构模式:明确将命令与查询分开,使应用程序内存(事件日志)成为事实的唯一来源。
 
banq注:体现系统备忘录记忆模式的设计不只是事件溯源,还有Git和区块链,区块链这种分布式账本本身就是一种记录记忆模式,任何一笔交易的上文:前几笔交易,以及后续下文的几笔交易都可以通过区块链的链数据查询到,DDD+CQRS+ES是以类似方式实现,思路是一致的,可以使用DDD思路来判断哪些场合能够使用区块链技术,这解决了区块链用例场景的判断问题,区块链本身的加密以及工作证明的复杂性会限制判断其用途。见下面帖子如何用DDD聚合判断区块链是否可以使用某个业务场景:
美国纽约州将新冠健康通行证与区块链结合是一种炒作?
lakeFS:实现类似于Git或事件溯源ES的对象存储功能