CQRS本身就是一个非常简单的模式。他只是规定了应用程序的命令处理的组件(增,删,改)应该和查询的组件进行分离。尽管这种分离模式本身非常简单,但是当与其他模式结合在一起时,它就提供了许多非常强大的功能。Axon提供了可以配合CQRS使用且很轻松地实现各种模式的构建块。
下图显示了基于CQRS的事件驱动架构的扩展布局示例。显示在左侧的UI组件以两种方式与应用程序的其余部分进行交互:向应用程序发送命令(如顶部分所示),并向应用程序查询信息(如底部分所示)。
命令(Command)通常由比较简单的对象来表示,这些对象包含了command handler(处理command的程序)要处理该命令所需的所有数据。一个命令通常用它的名字来表达它的意图。用Java语言来说,这意味着类名是用来表明需要做什么的,而类里的字段则提供所需的数据。
当Command Bus(命令总线)收到command的时候会把他路由到对应的Command Handlers。每个Command Handler只会接收他指定类型的command并根据command里的内容来执行相应的业务逻辑。然而,在某些情况下,无论命令的实际类型(如验证,日志记录或授权)是什么,他还是要执行对应的逻辑。
Command Handler从repository (仓储,在DDD中他们将持久化数据的地方抽象出的一个概念)获取到领域对象(Aggregates,聚合),然后在领域对象执行对应的方法(method)从而来改变它们的状态。这些聚合通常包括具体的实际业务逻辑,因此他们须要维护自己内部的数据一致性。聚合的状态变化会导致产生领域事件。领域模型由领域事件和聚合组成。
仓储(repository)负责提供对聚合根的访问。通常情况下,这些仓储库只会针对通过其唯一标识符来查找聚合进行优化。一种方式是,仓储存储的是聚合本身的状态(例如使用对象关系映射),而另一种是,仓储通过Event Store存储聚合在各种行为中产生的真实事件。仓储还负责持久化聚合产生的事件。
Axon提供了对直接持久化聚合(例如使用对象关系映射方式)和事件溯源 (event sourcing)的支持。
事件总线(Event bus)会将事件分发给所有感兴趣的事件监听者。他可以是同步方式也可以是异步方式。异步方式允许事件发送出去后就直接返回并将控制权交给用户,而此时的后台应用程序也会对事件进行处理。这样好处就是用户无需等待事件处理完成后才能做其他的事,所以应用程序的响应速度就会更快。另一方面,同步事件处理更简单且符合常理。默认情况下,同步处理方式会将command的处理和event的处理放进同一个事务中。
Event listeners接收事件并处理它们。-些hanndler的操作是更新数据源的数据,而另一些是将消息发送到其他外部系统。您可能会注意到,command handlers完全不知道接下来会发生什么事。这意味着对扩展应用程序新功能是非入侵式的。所以你需要做的是添加另一个事件监听器(event listener)。事件将应用程序中的所有组件很松散地耦合在一起。
在某些情况下,事件处理需要将新命令发送给应用程序。例如,当我们收到一个订单,这可能意味着客户的账户应该扣掉买商品的钱,并且必须告知运送方准备运送所购买的商品。在许多应用中,逻辑将变得比这更复杂:如果客户没有及时付款呢?您会立即寄出货物,还是先等待付款?saga是CQRS的概念,它负责管理这些复杂的业务事务。
Axon 框架从3.1版本开始提供了组件来处理查询(query)。查询总线(Query Bus)收query并将其路由到查询处理Handler(Query Handlers)。query handler会到查询总线上注册,同时会带上他能处理的query类型及响应类型。查询和结果类型通常都是简单的只读DTO对象。这些DTO的内容通常由用户界面的决定。在大多数情况下,它们直接映射到UI中的特定视图(也称为“按视图”)。
可以为相同类型的查询和响应类型注册多个Query handler。当分发query的时候,客户端可以指定他是想要一个结果还是来自所有可用的query handlers的结果。
Axon模块结构
Axon Framework包含了很多模块来应对CQRS的特定问题领域。根据你项目的使用情况,可以自己选择一个或者多个模块。
从Axon2.1起,所有模块兼容OSGi。这意味着它们在manifest文件包含所需的头文件,并声明它们导入和导出的包。目前,只有Slf4J bundle(1.7.0 <= 版本< 2.0.0)是必需的。所有其他导入的都被标记为可选的。
主要模块
Axon的主要模块是经过全面测试的模块,足够坚固,可用于苛刻的生产环境。所有这些模块的maven groupId是org.axonframework。
Core模块正如其名一样,他是Axon的核心模块。如果您使用单节点设置,则该模块可能会提供您需要的所有组件。所有其他的Axon模块都依赖于这个模块,所以它必须始终在classpath可用。
Test模块包含测试功能,可用于测试基于Axon的组件,如Command Handlers, Aggregates and Sagas。您通常在运行时不需要此模块,只需在测试期间将其添加到classpaths中即可。
分布式CommandBus模块包含可用于在多个节点上分发命令的实现。它使用JGroups和Spring Cloud Connectors来连接其他节点。
AMQP模块提供的组件允许您使用基于AMQP的消息中间件作为分发机制来构建EventBus。即使Event Handler暂时不可用,也可以保证传送。
Spring模块允许在Spring应用程序上下文中配置Axon组件。它还提供了许多特定于Spring Framework的构建块实现,例如用于在Spring消息通道上发布和检索Axon事件的适配器。
MongoDB是一个基于文档的NoSQL数据库。 Mongo模块提供了Event和Saga Store实现,它们将事件流和saga存储在MongoDB数据库中。
几个AxonFramework组件提供监视信息。Metrics模块提供了基于Codehale的基本实现来收集监控信息。
使用Axon API
CQRS是一种架构模式,因此无法提供适合所有项目的单一解决方案。很明显,Axon框架并不试图提供这种解决方案。取而代之的是,Axon提供了遵循最佳实践的实现方法,以及根据您的具体要求调整每个实现的方法。
几乎所有的基础设施构建块都会提供钩子点(例如拦截器,解析器等),允许您将特定于应用程序的行为添加到这些构建块。在很多情况下,Axon将为那些适合大多数用例的钩子点提供实现。如果需要的话,你可以简单地实现你自己的。
非基础设施对象,如消息,通常是不可变的。这确保了这些对象可以安全地在多线程环境中使用,没有副作用。
为确保定制最大化,所有的Axon组件都使用接口进行定义。抽象和具体的实现类你可以根据你自己的情况选择使用,不会影响到框架的依赖。你甚至可以使用该接口来构建你自己想要的实现。
Spring支持
Axon框架为Spring提供了广泛的支持,但强制您使用Axon的时候也必须要用Spring。所有组件都可以以编程方式进行配置,并且不需要把Spring放在classpath路径中。但是,如果你使用Spring,使用Spring的注解支持,许多配置变得更加容易。