CQRS & Event Sourcing — 解决检索应用程序状态问题的一剂良方

如今,每一个开发人员都很熟悉MVC标准体系结构设计模式。大多数的应用程序都是基于这种体系结构进行建立的。它容许咱们建立可扩展的大型企业应用程序,但近期咱们还听到了另外的一些有关于CQRS/ES的相关信息。这些方法应该被放在MVC中一块儿使用吗?他们能够解决什么问题?如今,让咱们一块儿来看看CQRS/ES是什么,以及他们都有哪些优势和缺点。数据库

CQRS — 模式介绍

CQRS(Command Query Responsibility Segregation)是一种简单的设计模式。它衍生与CQS,即命令和查询分离,CQS是由Bertrand Meyer所设计。按照这一设计概念,系统中的方法应该分为两种:改变状态的命令和返回值的查询。Greg young将引入了这个设计概念,并将其应用于对象或者组件当中,这就是今天所要将的CQRS。它背后的主要思想是应用程序更改对象或组件状态(Command)应该与获取对象或者组件信息(Query)分开。设计模式

下面,将通一张图来讲明应用程序中有关CQRS部分的组成结构:服务器

CQRS模式介绍

Commands(命令)—表示用户的操做意图。它们包含了与用户将要对系统执行操做的全部必要信息。markdown

  • Command Bus(命令总线):是一种接收命令并将命令传递给命令处理程序的队列。
  • Command Handler(命令处理程序):包含实际的业务逻辑,用于验证和处理命令中接收到的数据。Command handler负责生成和传播域事件(Event)到事件总线(Event Bus)。
  • Event Bus(事件总线):将事件发布给订阅特定事件类型的事件处理程序。若是存在连续的事件依赖,事件总线可使用异步或者同步的方式将事件发布出去。
  • Event Handler(事件处理程序):负责处理特定类型的事件。它们的职责是将应用程序的最新状态保存到读库中,并执行终端的相关操做,如发送电子邮件,存储文件等。

Query(查询):表示用户实际可用的应用程序状态。获取UI的数据应该经过这些对象完成。架构

下面咱们将介绍有关CQRS的诸多优势,它们是:app

  • 咱们能够给处理业务逻辑部分和处理查询部分的开发人员分别分配任务,但须要当心的是,这种模式可能会破坏信息的完整性。
  • 经过在多个不一样的服务器上扩展Commands和Query,咱们能够进一步提高应用程序的读/写性能。
  • 使用两个不一样的数据库(读库/写库)进行同步,能够实现自动备份,无需额外的干预工做。
  • 读取数据时不会涉及到写库的操做,所以在使用事件源是读数据操做会更快。
  • 咱们能够直接为视图层构建数据,而无需考虑域逻辑,这能够简化视图层的工做并提升性能。

尽管使用CQRS模式具备上述诸多的优势,可是在使用前还须要慎重考虑。对于只具备简单域的简单项目,其UI模型与域模型紧密联系的,使用CQRS反而会增长项目的复杂度和冗余度,这无疑是过分的设计项目。此外,对于数据量较少或者性能要求较低的项目实施CQRS模式不会带来显著的性能提高。框架

Event Sourcing — 案例研究

有这样一个案例,咱们想要检索任何一个域对象的历史状态数据,并且在任什么时候间均可以生成统计数据。咱们想要检查上个月、上个季度或者过去任什么时候间的状态汇总。想要解决这个问题并不容易。咱们能够在特定的时间范围内将额外的数据保存在数据库中,但这种方法也存在一些缺点。咱们不知道范围应该是什么样子,以及将来统计数据须要哪些数据项。为了不这些问题,咱们能够天天为全部聚合建立快照,但它们一样会产生大量的冗余数据。异步

Event Sourcing(ES)彷佛是目前解决这些问题的最佳方案。Event Sourcing容许咱们将Aggregate(聚合)状态的每个更改事件保存在Event Store的事件存储库中。经过Command Handler将事件写入到事件存储库中,并处理相关的逻辑。要建立Aggregate(聚合)对象的当前状态,咱们须要运行建立预期域对象的全部事件并对其执行全部的更改。下面咱们将经过一张图来讲明这一架构设计方式:oop

event-sourcing

下面咱们将列举一些使用ES的优势:post

  • 时间穿梭机:能够及时重建特定聚合的状态。每一个事件都包含一个时间戳。根据这些时间戳能够在特定的时间内运行事件或者中止事件。
  • 自动审计:咱们不须要额外的工做就能够检查出在特定的时间范围内谁作了什么以及改变了什么。这和能够显示更改历史记录的系统日志不一样,事件能够告知咱们每次更改背后所对应的操做意图。
  • 易于引入纠正措施:当数据库中的数据发生错误时,咱们能够将应用程序的状态回退到特定的时间点上,并重建当时的应用程序状态。
  • 易于调试:若是应用程序出现问题,咱们能够将特定事件内的全部事件取出,并逐条的重建应用状态,以检查应用程序可能出现问题的地方。这样咱们能够更快的找到问题,缩短调试所需的时间。

Aggregates

**Aggregate(聚合)**一词在本文中屡次被说起,那它究竟是什么意思?**Aggregate(聚合)**来自于领域驱动设计(DDD)的一个概念,它指的是始终保持一致状态的实体或者相关实体组。咱们能够简单的理解为接收和处理Command(包含Command Handler)的一个边界,而后根据当前状态生成事件。在一般状况下,Aggregate root(聚合根)由一个域对象构成,但它能够由多个对象组成。咱们还须要注意整个应用程序能够包含多个Aggregate(聚合),而且全部事件都存储在同一个存储库中。

总结

CQRS/ES能够做为特定问题的解决方案。它能够在标准N层架构设计的应用程序的某些层中进行引入,它能够解决非标准问题,常规架构中咱们所拿到的是最终状态,在不少状况下,当然当前状态很重要,但咱们还须要知道当前状态是如何产生的。CQRS和ES两种概念应该一块儿使用吗?事实代表,并无。咱们想要统计任什么时候间范文内的域对象状态,而写库只能存储当前状态。引入CQRS并没能帮助咱们解决这一问题。在下一章节中,咱们将引入Axon框架,Axon框架时间了CQRS/ES,用于解决某些域对象的一些特定问题,尤为是收集历史统计数据。咱们将阐述如何使用Axon框架实现CQRS/ES并实现与Spring Boot应用程的整合。

做者:LukaszKucik ,译:谭朝红,原文:CQRS and Event Sourcing as an antidote for problems with retrieving application states

访问个人博客:罗摩尔-Ramostear 获取更多信息