我怀疑这可能是那种实际上SQL数据库才是正确选择而尝试使用NoSQL是错误的情况之一,但是当我想到我对CQRS和事件溯源的了解很少时,我想知道实体/文档实际上是“账户”,而交易是针对它存储的“事件”,并且当发生这些“事件”时,我的应用程序是否还会写入到一个易于查询的读取存储中,例如SQL数据库。
非常感谢。
我个人认为这是一个好主意,但我有一点偏见,因为我的全职工作是构建基于CQRS、事件源和文档数据库的会计系统。
原因如下:
事件源和会计基于相同的原理。你不删除任何东西,只修改。如果添加了一个错误的交易,你不会删除它,而是创建一个抵消交易。事件也是一样,你不会删除它们,而是创建一个取消第一个事件的事件。这意味着你要发布很多TransactionAddedEvent。
接下来,如果你在进行复式记账,记录交易的方式与你在屏幕上查看它的方式是不同的(特别是在资产负债表中)。因此,我对cqrs又很喜欢。我们可以使用正确的会计原则存储数据,但我们的读取模型可以优化,以显示你想要查看的数据。
在资产负债表中,你希望查看给定账户的所有条目。你不想看到交易,因为交易有两个方面。你只想看到影响该账户的条目。
所以,在你的文档数据库中,你会有一个entries集合。
这使得查询非常容易。如果你想看到一个账户的所有条目,你只需说SELECT * FROM Entries WHERE AccountId = 1。我知道这是SQL,但每个人都可以理解这个查询的简单性。在文档数据库中,它同样容易。而且,它将非常快。
然后,你可以创建一个平衡表,通过对accountid分组并对日期设置限制来实现。注意,根本不需要使用连接,这使得文档数据库成为一个很好的选择。
Entries
“表”只是用作读模型?也就是说,一个事件可能涉及多个账户(通常是2个,但有时更多),但正如您所说,当您想要查看单个账户的摘要时,唯一感兴趣的是涉及摘要账户的交易部分。 - dpwr理论与架构
如果你深入挖掘会计理论和历史,就会发现“文档”应该是源头文件——采购订单、发票、支票等等。会计记录是通常可读的源文件的标准汇总。会计交易是两个或多个记录命中两个或多个账户,通过借方和贷方相平衡来绑定在一起的。账户余额,诸如资产负债表和损益表之类的报告都是这些交易的摘要。
把它想象成一个分层的架构——底层基础是源文件。如果源文件是电子的,那么它进入会计系统的文件存储层。这里可能会用到nosql数据库。如果源文件是纸张,则将其成像并/或将其与索引号一起存储在会计系统的文档层中。再上一层是数字记录,对这些文件进行汇总;每个文件由一个或多个未平衡的交易腿汇总而成。再往上一层是平衡交易;每个交易由两个或多个这些未平衡的交易腿组成。最上面一层是汇总这些平衡交易的财务报表。
源文件和外部应用程序
源文件是“唯一的真相”——而不是描述它们的记录。你应该总是能够从源文件中重建整个数据库。在某种程度上,数据库本身只是指向源文件的索引。然而,过多的人忽略了这一点,编写的会计软件将交易本身视为真相来源。这就需要另一个存储和工作流系统来处理源文件本身,最终导致典型的现代企业混乱。
这意味着任何写入会计系统的应用程序都只应创建源文件并将其添加到底层。但实际情况是,这一步常常被忽略,应用程序直接创建交易。这意味着源文件不再在会计系统中,而是在创建交易的应用程序中。这样会很脆弱。
事件、工作流和数字化
如果你正在使用某种事件模型,那么使用事件的正确场所是将源文件附加到事件上。事件随后触发将该文档解析为正确的会计记录。如果源文件已经是数字格式,则可以自动完成解析;如果源文件是纸张或未格式化的消息,则需要手动解析。这听起来像是工作流系统的开始,对吧?尽管如此,你仍然希望保留原始的源文件。文档数据库似乎是个好主意,尤其是如果它支持一种模式,可以将源文件与其结果解析和平衡的记录相互绑定。
您可以创建这样的系统。 在这种情况下,您有帐户聚合和时间段聚合。 时间段通常是一个月、一个季度或一年。 在每个时间段内,您都有该期间的交易。 这意味着加载当前状态非常快,而且您拥有完整的日志,可以向后查看。 时间段的原因是这通常是您实际考虑此类事物的边界。