事件采购作为许多事情的奖励,例如事件历史/审计跟踪,完整和一致的视图再生等.听起来很棒.我是粉丝.但这些是读取端实现细节,您可以通过将事件存储完全移动到读取端作为另一个订阅者来完成相同的操作..那么为什么不呢?
这里有一些想法:
这似乎是将其保持在写入方面的一个技术论据.这是来自Greg Young的http://codebetter.com/gregyoung/2010/02/20/why-use-event-sourcing/:
但是,使用存储当前状态快照的东西存在一些问题.最大的问题围绕着您为数据引入了两个模型这一事实.您有一个事件模型和一个表示当前状态的模型.
我觉得有趣的是"快照"这个词,它最近也成为了事件采购中的一个杰出术语.在写入端引入事件存储会增加加载聚合的一些开销.您可以讨论多少开销,但这显然是一个感知或预期的问题,因为现在有从快照加载聚合的概念以及自快照以来的所有事件.所以现在我们又......两个模型了.不仅如此,我所看到的快照建议旨在实现为基础设施泄漏,后台进程遍历整个数据存储以保持高性能.
拍摄快照后,快照之前的事件从写入视角变为100%无用,除了...重建读取端!这似乎是错的.
另一个性能相关主题:文件存储.有时我们需要将大型二进制文件附加到实体.从概念上讲,有时这些与实体相关联,但有时它们是实体.将它们放在事件存储中意味着每次加载实体时都必须物理加载这些数据.这已经够糟糕了,但想象一下这些中的几个或几百个.我所看到的每一个答案都是基本上咬了一口子并将uri传递给文件.这是一个警察,破坏了分布式系统.
然后是维护.重建视图需要涉及事件存储的过程.因此,现在您编写的每个视图维护任务都会将您的写入模型绑定到使用事件存储...永远.
CQRS的重点不在于读取模型和写入模型的用例从根本上是不兼容的吗?那么我们为什么要把读取模型的东西放在写入端,牺牲灵活性和性能,并再次将它们重新耦合.为什么要花时间?
总而言之,我很困惑.在我所处的所有方面,事件存储作为阅读模型细节更有意义.您仍然可以实现保留事件存储的诸多好处,但是您不会过度抽象写入持久性,可能会降低灵活性和性能.并且您不会通过泄漏的抽象和维护任务将您的读/写端备份起来.
那么有人可以向我解释一个或多个令人信服的理由将其保留在写入方面吗?或者,为什么它不应该作为维护/报告问题进行阅读?我再次质疑商店的实用性.它应该去哪里:)
假设Book和Author是在我的模型总根源.
在阅读模型中,我有一个表格AuthorsAndBooks,其中列出了作者和书籍Book.AuthorId
当BookAdded事件被触发时,我想要接收Author数据以创建新AuthorsAndBooks行.
因为Book是聚合根,所以有关信息Author不包含在BookAdded事件中.我不能包括它,因为Authorroot没有getter(根据所有关于CQRS和Event Sourcing的示例和帖子的指南).
通常我会在这个问题上收到两种答案:
Author从View Model 加载并使用它来构建AuthorsAndBooks行.最后一个在并发方面存在一些问题.BookAdded处理事件时,视图模型中无法使用作者数据.
你用什么方法来解决这个问题?谢谢.
我认为AccountingTransaction是一个聚合根,因为它保护了不变量:
没有钱泄漏,它应该从一个帐户转移到另一个帐户.
public class AccountingTransaction {
private String sequence;
private AccountId from;
private AccountId to;
private MonetaryAmount quantity;
private DateTime whenCharged;
public AccountingTransaction(...) {
raise(new AccountingEntryBookedEvent(sequence, from, quantity.negate(),...);
raise(new AccountingEntryBookedEvent(sequence, to, quantity,...);
}
}
Run Code Online (Sandbox Code Playgroud)
将AccountingTransaction添加到其存储库时.它发布了几个AccountingEntryBookedEvent,用于更新查询端的相应帐户余额.
每个db事务更新一个聚合根,最终一致性,到目前为止一直很好.
但是,如果某些帐户应用转移限制,例如无法将数量转移到当前余额,会怎样?我可以使用查询方来获得帐户的余额,但我担心来自查询方的数据是陈旧的.
public class TransferApplication {
public void transfer(...) {
AccountReadModel from = accountQuery.findBy(fromId);
AccountReadModel to = accountQuery.findBy(toId);
if (from.balance() > quantity) {
//create txn
}
}
}
Run Code Online (Sandbox Code Playgroud)
我应该在命令端建模帐户吗?我必须为每个数据库事务更新至少三个聚合根(从/到帐户和帐户txn).
public class TransferApplication {
public void transfer(...) {
Account from = …Run Code Online (Sandbox Code Playgroud) 我想知道域事件应该是多么精细?
例如,我有一些简单的事情,比如更改个人资料页面上的firstName,secondName和电子邮件地址.我应该有3个不同的域事件还是只有一个?
通过粗粒度域事件,当我添加新功能时,我必须创建一个新版本的事件,所以我必须添加一个新的事件类型,或在事件存储中存储事件版本.通过细粒度的域事件我没有这些问题,但我有太多的小类.您怎么看?在这种情况下,最佳做法是什么?
如何解决在事件存储中删除数据的问题?
我需要永久和完全删除一些数据,以遵守隐私法.
我找到了这些替代方案:
加密需要删除的数据,并将加密密钥存储在自己的表中.当需要删除数据时,只删除加密密钥.
对不需要删除的数据使用事件源,参考CRUD数据库获取需要删除的机密数据.
有没有其他方法可以做到这一点?
问题:
两名员工(A&B)同时离线,同时编辑客户#123,比如版本#20,当离线继续进行更改时......
场景:
1 - 两名员工编辑客户#123并对一个或多个相同的属性进行更改.
2 - 两名员工编辑客户#123但不要做出相同的更改(他们互相交叉而不接触).
......然后他们都回到网上,第一名员工A追加,从而将客户更改为版本#21,然后将员工B更改为版本#20
问题:
我们在方案1中保留了哪些变化?
我们可以在方案2中进行合并吗?
语境:
1 - CQRS +事件采购风格系统
2 - 使用事件源Db作为队列
3 - 读模型的最终一致性
4 - RESTful API

编辑-1:到目前为止基于答案的澄清:
为了执行精细的粒度合并,我需要为一个表单中的每个字段设置一个命令?

在上面,ChangeName,ChangeSupplier,ChangeDescription等的细粒度命令,每个都有自己的时间戳,允许自动合并事件A和B都更新ChangedName?
编辑-2:根据特定事件存储的使用进行跟进:
好像我会利用@GetEventStore来保持我的事件流的持久性.
他们使用乐观并发如下:
流中的每个事件都将流版本增加1
写入可以指定预期版本,在编写器上使用ES-ExpectedVersion标头
-1指定流不应该已存在
0及以上指定流版本
如果流不在版本中,写入将失败,您要么使用新的预期版本号重试,要么重新处理该行为并确定如果您选择它就行.
如果未指定ES预期版本,则禁用乐观并发控制
在这种情况下,Optimistic Concurrency不仅基于消息ID,还基于事件#
domain-driven-design occasionallyconnected cqrs event-sourcing get-event-store
当您希望代码在竞争条件下工作时,通常开发人员使用乐观并发控制(OCC).来自维基百科:
...在提交之前,每个事务都会验证没有其他事务修改了它已读取的数据.如果检查显示有冲突的修改,则提交事务将回退...
实现OCC的方法是检查version要修改的数据.如果版本不同,那么其他事务已修改数据,并由应用程序决定如何解决冲突(重新尝试,通知用户......).
草案如下:
class Repository
{
public class save($data)
{
$currentVersion = $data->version;
$data->version = $currentVersion + 1;
$result = $this->db->update($data, [
'id' => $data->id,
'version' => $currentVersion
]);
if (1 === $result) {
// everything ok
} else {
// conflict!
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,因为EventSourcing我们只追加域中发生的所有事件,我们不能再使用这种方法来实现OCC.在使用EventSourcing时,哪些其他方法可以保留OOC?
一个可行的选项,它可以在存储时查找冲突事件.这种方法允许对事件进行细粒度控制.我不知道这是否会使解决方案复杂化,或者我认为这是一个"标准",我在http://danielwhittaker.me/2014/09/29/handling-concurrency-issues-cqrs-event-sourced指出-系统/
问题描述中的任何空白都是值得赞赏的.提前致谢!
我正在研究事件基础架构,并了解了两种架构事件溯源和事件驱动架构。
我的理解如下
事件驱动 :
事件溯源:
但我不明白的是,为什么在事件源中重播事件会导出整个事务,但在事件驱动中是不可能的。
如果有人用现实生活中的例子来描述它将会很有帮助。
我正在进行一个新项目的研究阶段,目前我正与一位工作同事就该项目的架构进行辩论.
我们已经同意我们将使用CQRS和使用azure的事件源来创建分布式消息传递系统.它将是一个SPA,前端使用角度js,后端将是Web API.
我们现在讨论了如何设置数据库,这就是发生差异的地方.
我们同意将数据库拆分为两个数据库,一个用于读写.我的同事想要将sql server用于读写数据库,因为他在sql中度过了他的整个职业生涯并且除了sql之外不想听到任何其他内容.另一方面,我一直在研究NoSql,我觉得它适合读取数据库,因为它更适合性能.
由于CQRS完全是关于最终的一致性,我已经读过NoSql数据库也基于此,现在让我考虑将NoSql用于写入数据库.
我们还计划为每个聚合根创建一个事件表,而不是一个包含所有事件的通用事件表.由于这些表不是关系型的,因此我想到为什么我们应该使用sql server.
我的问题更多的是人们如何创建活动商店的最佳实践或一般方法.
CQRS(命令查询责任隔离)和事件采购有什么区别?
我相信Event Sourcing是一种CQRS.每种产品的区别是什么,以及什么使得Event Sourcing与其他类型的CQRS不同?
谢谢,
event-sourcing ×10
cqrs ×9
event-store ×2
apache-kafka ×1
c# ×1
event-driven ×1
events ×1
java ×1
nosql ×1
saga ×1