什么时候应该选择 CQRS 而不是基于 CRUD的RESTful API? - codeopinion

21-08-31 banq

使用 CRUD,当您通过创建、更新、删除进行状态更改时,您并没有明确捕获它发生的原因。例如,如果您正在对客户执行更新,为什么会发生这种情况?他们的地址变了吗?他们的贴现率有变化吗?使用基于 CRUD 的方法,您并不确切知道,您必须根据所做的更改来暗示它。

使用 CQRS,您的所有命令都会使这些状态更改变得明确。您不是在“更新客户”,而是在使用诸如 ChangeCustomerAddress 或IncreaseDiscountRate 之类的显式命令。由于命令是显式的,因此您可以从这些命令推断要发布的各种事件。

如果您想发布基于 CRUD 的事件,您将拥有非常通用的事件,例如 CustomerUpdated。如果您使用 CQRS,您将拥有诸如 CustomerAdresssChanged 和 CustomerDiscountRateIncreased 之类的事件。

 

SOA 和微服务有什么区别?

我相信这是因为我如何呈现与微服务相关的事件驱动架构。这个问题的答案实际上取决于您对 SOA 和微服务的定义。我一般使用的微服务的定义来自 Adrian Cockcroft:

具有有界上下文的松散耦合面向服务架构。
https://www.slideshare.net/adriancockcroft/dockercon-state-of-the-art-in-microservices

如果我剖析这个定义,就是松散耦合的面向服务的架构。松散耦合意味着事件驱动架构。

在我看来,SOA 一直是松散耦合的,但当时一般开发人员社区可能不是这样理解的。

定义的有界上下文部分来自领域驱动设计。有界上下文是关于定义边界的。服务应该拥有一组业务能力和这些能力背后的数据。您不会在服务之间共享行为或数据。服务是独立的。并且因为它们是独立的,所以通过事件或消息驱动架构以松散耦合的方式完成通信。

所以我对这个问题的回答是两者没有什么不同。

 

如果有一个跨越多个有界上下文的分布式事务是反模式的,Saga是一种反模式吗?

不,Saga不是反模式。它负责在没有分布式事务的多个服务之间协调长期运行的业务流程。由于可能存在故障,saga 负责通过向适当的服务发送补偿操作来处理故障。Saga 是一个集中处理长期运行的业务流程的工作流的地方。当我说长期运行的业务流程时,这可能意味着几毫秒到几周。

Saga 方案是使用事件编排,它消除了编排的集中逻辑。事件编排是让所有服务消费和产生最终满足长期运行的业务流程的事件。

 

事件溯源时如何处理版本控制?

版本控制的最终答案是Greg Young 在 Event Sourced System 电子书中的版本控制

我经常喜欢将其与关系数据库进行比较。如果要向表中添加新列,则需要将该列设为可为空或为其提供默认值,然后可能会回填现有数据。

使用事件溯源,在现有事件上创建一个新属性可以为空是完全相同的。您需要处理为 null 的值。如果它不为空并且具有默认值,则情况仍然与关系数据库相同。不同之处在于您不能回填和更新现有流中的旧事件。在这种情况下,您可以做的是确保任何旧事件都可以在运行时上转换为新事件。

 

事件溯源时的最佳实践和陷阱?

最大的缺陷是这个概念与那些只做过 CRUD 并且只记录当前状态的开发人员有多么不同。

虽然我认为 Event Sourcing 的概念并不难,但处理不可变日志(事件存储)的方式与仅记录当前状态有很大不同。

这方面的一个例子是,在使用关系数据库时,如果您需要“修复”数据,您只需编写一个 UPDATE 语句来更新数据库中的记录。使用事件溯源,由于事件位于不可变日志(事件流)中,您不能只是“更新”现有事件。这实际上甚至没有多大意义。您通常必须创建补偿事件来“撤消”某些事情。

在典型的例子中,如果您将 10 美元存入银行账户,则该存款被错误地记录为 100 美元。银行实际上会创建 100 美元的提款,然后是 10 美元的存款。这意味着他们会完全撤销您最初的错误存款。

这与我相信大多数开发人员习惯的不同。正如在上一个问题中提到的,版本控制是另一个与大多数人习惯的非常不同的主题。

 

如何处理最终一致性预测的最终一致性(读取模型)

在大多数事件源系统中,投影用于创建经常用于特定用例的不同读取模型。

然而,这个问题甚至不需要特定于事件源,而是您查询的任何数据源最终都是一致的。例如,落后于主副本的只读副本。

我认为这里的陷阱是你如何设定用户的期望。如果用户执行他们知道正在改变系统状态的操作,但是在他们执行操作后,您没有向他们提供显示更新状态的数据,他们将不会有良好的体验。他们期望一致性。

从技术角度来看,使用版本(数字)。当您执行查询时,在对调用者的响应中返回一个版本号。当调用者需要重新查询时,他们会知道是否发生了更新,因为版本会发生变化。如果没有,您可以等待并重试再次获取数据。

 

 

4
猜你喜欢