CQRS和事件溯源的基本原理
首先/最开始
我是Air Closet的工程师Ain。
这篇文章是Air Closet Advent Calendar 2023第六天的文章。
CQRS:命令查询职责分离
CQS(Command Query Separation)设计模式的目的是将方法分为两个主要组。
-
- 命令(Command):改变对象数据但不返回任何内容或仅返回元数据的组。
-
- 查询(Query):返回信息但不改变对象数据的组。
- 根据这些,实际的方法不会同时担当查询和命令的角色。让我们看一个典型的Javascript示例:
进一步进行,将一个数据库分割成两个独立的数据库(“读取数据库”和“写入数据库”).
利用CQRS模式开发API。
在使用CQRS开发API时,不仅需要将routes分为POST和GET,还需要确保命令不会返回任何数据。如果需要返回数据,只需要返回元数据级别。
另一个问题是,“如果不执行查询,将无法确定命令是否成功执行”。
我认为可以利用“第三方API”(例如:Events API)通过Web socket或者HTTP Streaming执行推送通知,并通过事件判断命令是否成功执行。
在图形世界中有三个概念。
-
- Query
-
- Mutation
-
- Subscription
-
-
-
- これら3つの概念は以下のCQRSの概念それぞれに対応する
-
-
-
- Query
-
- Command
-
- Event API
-
-
-
- だからこそ GraphQLはCQRS実施するツールと認められます。
-
CQRS、DDD和EventSourcing
CQRS常常与DDD和EventSourcing一起使用,虽然本质上各有不同,但彼此能够很好地互相补充。
发送到基于CQRS的应用程序的命令API的命令也可以被解释为聚合的命令,按照DDD的意义。
然后,聚合会生成一个或多个领域事件,这些事件会使用事件溯源保存在事件存储中,并且稍后可以用于回放聚合。
此外,领域事件也会传送到应用程序的读取页面,在那里更新预计算的视图。为了实现这个目的,所谓的投影会被使用。它根据技术领域的事件来决定哪些视图与之有关联,并相应地调整受影响的视图,使用CRUD语句进行调整。
CAP规则
CAP规则是什么?
Consistency: 一貫性
Availability: 可用性
Partition Tolerance: パーティション耐性
分布式系统通过在将写入操作返回给客户端之前将其反映到所有数据库节点中来确保一致性。这样,系统中的所有节点都会返回唯一的响应。
可用性表示了系统能够处理所有写入和读取请求,任何时点都没有等待时间,并且不会拒绝系统请求的能力。
分区容错性保证了系统在节点故障或节点间连接丢失的情况下仍能继续运行。
实际上,分布式系统通常只能保证三个特性中的两个。也就是说,可以保证CA(一致性和可用性)、CP(一致性和分区容错性)或者AP(可用性和分区容错性)中的任意一种。这被称为CAP定理,是由Eric Brewer提出的,对于分布式系统的构建和部署来说是一个重要的方面。
一致性的缺失被视为最危险的情况之一。
为什么选择CQRS?
这个问题的一个典型原因是它与DDD和事件溯源等其他概念相辅相成。
CQRS将读操作和写操作明确分开,这对微服务架构有很多好处。
此外,DDD和事件源的良好结合可以更清晰且更容易地分离技术代码和业务代码。
当然,CQRS有很多优点,但我认为它也有一些缺点。其中一个缺点是实施起来很困难,但是目前有许多框架,比如NestJS(typescript,nodejs),强力支持CQRS。
事件源(Event-Sourcing)是一种软件开发模式。
事件溯源通常用于领域驱动设计(DDD)和命令查询责任分离(CQRS)中作为数据保存机制。尽管这些概念是独立的,但它们能完美地相互补充。
数据的保存通常使用CRUD操作进行。这里有四个主要操作与数据库相关(创建、读取、更新、删除)。其中,更新和删除这两个操作属于破坏性操作,与其他两个操作不属于同一类别。
在中文中,DELETE当然会像它的名字所示,删除数据。
而在UPDATE中,旧数据会被删除,并用新数据来替换。
因此,事件源使用CREATE和READ来代替UPDATE和DELETE。
在事件溯源中,所有的更改都作为新条目添加到列表中,并且这些条目永远不会被更改或删除。
因此,Event-Sourcing数据库通常称为Event-Store,随后的事件会被重新加载和聚合,以重新生成数据(可能是在当前时间或某个过去的时间点)。
事件溯源的缺点
由于事件被添加到事件存储中,因此事件存储将不断增大,结果可能导致复原数据所需的聚合过程变慢。
太多事件记录的问题可以通过快照解决。
CRUD与事件溯源进行比较
CRUD具有保存原始数据时的高性能等独特优势。无需特定逻辑。
然而,CRUD的明显缺点之一是数据竞争状态,即事务可能互相覆盖。而在事件溯源中,这种数据竞争状态较少。原因在于事件记录仅以增量形式保存数据。
在事件溯源中,使用哪种数据库?
事件溯源仅需INSERT和SELECT操作,而无需JOIN或CTE等复杂查询。因此,对于事件溯源数据库的要求也相当低。
将事件以BLOB或JSON格式保存具有许多优点。因此,NoSQL数据库(如MongoDB)是保存数据时以JSON格式的优秀选择,因为它不需要架构或数据组织。
此外,尽管Apache Kafka也可以作为事件存储的选择,但仅在您充分了解自己的情况时才应使用Apache Kafka。
DDD和事件源架构(Event-Sourcing)
在DDD中,还有一个概念叫做领域事件。这个概念和事件溯源的事件概念都描述了与系统业务逻辑相关的事件。
由于通常情况下域事件是命令的结果,因此这些域事件会被保存在事件存储中。
总结
CQRS是一种对分布式系统非常有趣的方法,可以利用DDD和事件源的优势。尽管CQRS的引入比传统的客户端-服务器架构更复杂,但应用程序的未来扩展性将会增强。
尽管事件溯源在数据存储方面具有许多优点,但对于需要标准表单的数据或需要注意重复和唯一性的数据而言,事件溯源可能不是一个理想选择。
因此,事件溯源与CRUD不相匹配,反之亦然。然而,根据情况可能会互相补充。因此,请根据所面临的情况来决定选择哪种工具。
感谢您阅读有关CQRS和事件溯源的文章。期待与您在下一篇文章中再次相见。
此外,我们的航空公司也在进行工程师招聘活动,如果您对此有兴趣,请务必查看!