Command Query Responsibility Segregation,CQRS 这个架构好象最近博客园里讨论得比较多,有几篇园友的文章很有深度,推荐阅读:

比较有趣的是,以往一断谈及架构思路、OO这些,往往都是java大佬们的专长,而CQRS这个话题,好象.NET占了上风。园友汤雪华的ENODE开源大作,在github上人气也很旺。

于是,我逆向思路搜索了下java的类似项目,果然有一个AxonFramework,甚至还有一个专门的网站。按文档上的介绍,弄了一个hello world,记录一下:

CRQS是基于事件驱动的,其主要架构并不复杂,见下图:

简单来讲,对数据库的修改操作,UI层只管发送各种命令(Command),触发事件(Event),然后由EventHandler去异步处理,最终写入master DB,对于数据库的查询,则查询slave DB(注:这里的master db, slave db只是一个逻辑上的区分,可以是真正的主-从库,也可以都是一个库)。 这样的架构,很容易实现读写分离,也易于大型项目的扩展。

项目结构:

package的名称上大概就能看出用途:

command包定义各种命令,

event包定义各种事件,

handler包定义事件处理逻辑,

model包相当于领域模型

最外层的ToDOItemRunner相当于应用程序入口。

gradle依赖项:

group 'yjmyzz'

version '1.0'

apply plugin: 'java'

apply plugin: 'application'

sourceCompatibility = 1.8

repositories {

mavenLocal()

maven {

url 'http://maven.oschina.net/content/groups/public/'

}

mavenCentral()

}

dependencies {

testCompile group: 'junit', name: 'junit', version: '4.12'

compile "org.axonframework:axon:2.4.3"

compile "org.axonframework:axon-core:2.4.3"

compile "org.axonframework:axon-test:2.4.3"

compile 'org.springframework:spring-core:4.2.3.RELEASE'

compile 'org.springframework:spring-beans:4.2.3.RELEASE'

compile 'org.springframework:spring-context:4.2.3.RELEASE'

compile 'org.springframework:spring-context-support:4.2.3.RELEASE'

compile 'org.springframework:spring-aop:4.2.3.RELEASE'

compile 'org.springframework:spring-test:4.2.3.RELEASE'

compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.5'

compile 'org.apache.logging.log4j:log4j-core:2.5'

compile 'javax.persistence:persistence-api:2.1'

}

mainClassName='demo.axon.ToDoItemRunner'

command命令:

这里我们假设了二个命令:创建命令、完成命令

CreateToDoItemCommand

package demo.axon.command;

import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier;

public class CreateToDoItemCommand {

@TargetAggregateIdentifier

private final String todoId;

private final String description;

public CreateToDoItemCommand(String todoId, String description) {

this.todoId = todoId;

this.description = description;

}

public String getTodoId() {

return todoId;

}

public String getDescription() {

return description;

}

}

MarkCompletedCommand

package demo.axon.command;

import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier;

public class MarkCompletedCommand {

@TargetAggregateIdentifier

private final String todoId;

public MarkCompletedCommand(String todoId) {

this.todoId = todoId;

}

public String getTodoId() {

return todoId;

}

}

Event事件:

ToDoItemCreatedEvent

package demo.axon.event;

public class ToDoItemCreatedEvent {

private final String todoId;

private final String description;

public ToDoItemCreatedEvent(String todoId, String description) {

this.todoId = todoId;

this.description = description;

}

public String getTodoId() {

return todoId;

}

public String getDescription() {

return description;

}

}

ToDoItemCompletedEvent

package demo.axon.event;

public class ToDoItemCompletedEvent {

private final String todoId;

public ToDoItemCompletedEvent(String todoId) {

this.todoId = todoId;

}

public String getTodoId() {

return todoId;

}

}

EventHandler事件处理

package demo.axon.handler;

import demo.axon.event.ToDoItemCompletedEvent;

import demo.axon.event.ToDoItemCreatedEvent;

import org.axonframework.eventhandling.annotation.EventHandler;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class ToDoEventHandler {

Logger logger = LoggerFactory.getLogger(ToDoEventHandler.class);

@EventHandler

public void handle(ToDoItemCreatedEvent event) {

logger.info("We've got something to do: " + event.getDescription() + " (" + event.getTodoId() + ")");

}

@EventHandler

public void handle(ToDoItemCompletedEvent event) {

logger.info("We've completed a task: " + event.getTodoId());

}

}

上面的代码只是演示,将事件信息输出而已,真实应用中,这里可以完成对db的更新操作。

领域模型model

package demo.axon.model;

import demo.axon.command.CreateToDoItemCommand;

import demo.axon.command.MarkCompletedCommand;

import demo.axon.event.ToDoItemCompletedEvent;

import demo.axon.event.ToDoItemCreatedEvent;

import org.axonframework.commandhandling.annotation.CommandHandler;

import org.axonframework.eventhandling.annotation.EventHandler;

import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot;

import org.axonframework.eventsourcing.annotation.AggregateIdentifier;

public class ToDoItem extends AbstractAnnotatedAggregateRoot {

@AggregateIdentifier

private String id;

public ToDoItem() {

}

@CommandHandler

public ToDoItem(CreateToDoItemCommand command) {

apply(new ToDoItemCreatedEvent(command.getTodoId(), command.getDescription()));

}

@EventHandler

public void on(ToDoItemCreatedEvent event) {

this.id = event.getTodoId();

}

@CommandHandler

public void markCompleted(MarkCompletedCommand command) {

apply(new ToDoItemCompletedEvent(id));

}

}

然后让Spring将这些东西串在一起,配置文件如下:

1 <?xml version="1.0" encoding="UTF-8"?>

2

3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

4 xmlns:axon="http://www.axonframework.org/schema/core"

5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd6 http://www.axonframework.org/schema/core http://www.axonframework.org/schema/axon-core-2.0.xsd">

7

8

9

10

11

12 aggregate-type="demo.axon.model.ToDoItem"/>

13

14

15 aggregate-type="demo.axon.model.ToDoItem"

16 repository="toDoRepository"

17 command-bus="commandBus"/>

18

19

20

21

22

23

24

25

26

27

28

29

View Code

最后,提供一个舞台,让整个应用run起来:

package demo.axon;

import demo.axon.command.CreateToDoItemCommand;

import demo.axon.command.MarkCompletedCommand;

import org.axonframework.commandhandling.gateway.CommandGateway;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.UUID;

public class ToDoItemRunner {

private CommandGateway commandGateway;

public ToDoItemRunner(CommandGateway commandGateway) {

this.commandGateway = commandGateway;

}

public static void main(String[] args) {

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("sampleContext.xml");

ToDoItemRunner runner = new ToDoItemRunner(applicationContext.getBean(CommandGateway.class));

runner.run();

}

private void run() {

final String itemId = UUID.randomUUID().toString();

commandGateway.send(new CreateToDoItemCommand(itemId, "Need to do this"));

commandGateway.send(new MarkCompletedCommand(itemId));

}

}

输出结果:

12:01:36,113 INFO [main]: We've got something to do: Need to do this (3126f293-67fd-4bb7-b152-069acba775b6)

12:01:36,114 INFO [main]: Command executed successfully: demo.axon.command.CreateToDoItemCommand

12:01:36,205 INFO [main]: We've completed a task: 3126f293-67fd-4bb7-b152-069acba775b6

12:01:36,206 INFO [main]: Command executed successfully: demo.axon.command.MarkCompletedCommand

axon框架测试也很容易:

package test.demo.axon;

import demo.axon.command.CreateToDoItemCommand;

import demo.axon.command.MarkCompletedCommand;

import demo.axon.event.ToDoItemCompletedEvent;

import demo.axon.event.ToDoItemCreatedEvent;

import demo.axon.model.ToDoItem;

import org.axonframework.test.FixtureConfiguration;

import org.axonframework.test.Fixtures;

import org.junit.Before;

import org.junit.Test;

public class ToDoItemTest {

private FixtureConfiguration fixture;

@Before

public void setUp() throws Exception {

fixture = Fixtures.newGivenWhenThenFixture(ToDoItem.class);

}

@Test

public void testCreateToDoItem() throws Exception {

fixture.given()

.when(new CreateToDoItemCommand("todo1", "need to implement the aggregate"))

.expectEvents(new ToDoItemCreatedEvent("todo1", "need to implement the aggregate"));

}

@Test

public void testMarkToDoItemAsCompleted() throws Exception {

fixture.given(new ToDoItemCreatedEvent("todo1", "need to implement the aggregate"))

.when(new MarkCompletedCommand("todo1"))

.expectEvents(new ToDoItemCompletedEvent("todo1"));

}

}

given/when/expectEvents的意思是,给(given)一个事件,然后当(when)某个命令被调用时,期待(expectEvents)某个事件被触发。

java cqrs架构_CQRS框架:AxonFramework 之 Hello World相关推荐

  1. java cqrs架构_团队开发框架实战—CQRS架构

    团队开发框架实战-CQRS架构 CQRS架构图 261851438603372.jpg CQRS架构图.png 什么是CQRS? 这里只通过Udi Dahan的<Clarified CQRS&g ...

  2. java cqrs架构_简单聊聊CQRS

    序言 Domain Driven Design (DDD) CQRS Axonframework Mvc vs CQRS 参考 Domain driver design ​ 领域驱动设计 也就是我们在 ...

  3. 全新java架构技术框架Quarkus实战神仙文档

    前言 Quarkus是一款有别于传统Java架构的新技术框架.它建立在我们熟知的技术栈上,使用了诸多成熟的技术,如JPA.JAX-RS.EclipseVert.x.Eclipse MicroProfi ...

  4. 一线互联网技术:Java工程师架构知识系统化汇总,面完45K!

    根据高端招聘平台100 offer发布的Java人才盘点报告,在过去的2018年,Java仍然是最流行.招聘供需量最大的技术语言. 在此基础上,互联网行业针对 Java 开发的招聘需求,也是近年技术类 ...

  5. java培训分享:java培训架构师学习线路图

    本期java教程分享主要是讲解关于java培训架构师方面的内容,主要针对java架构师的学习线路图进行一个知识点的概括,下面来看看学习java架构师都需要了解哪些内容吧. java培训分享:java培 ...

  6. 从 Servlet 入手带你看架构和框架设计的套路

    以下代码相信大家都很熟悉,大学时学 Java Web 都写过这样的代码. 从第一次接触 Servlet 到之后的很长一段时间内,我都没理解 Servlet 是个什么玩意? 为什么要有 Servlet ...

  7. 清华学霸花了三年时间对java理解: Java分布式架构

    什么是分布式架构 分布式系统(distributed system)是建立在网络之上的软件系统. 内聚性是指每一个数据库分布节点高度自治,有本地的数据库管理系统. 透明性是指每一个数据库分布节点对用户 ...

  8. java restful netty_Java RESTful 框架的性能比较

    来源:鸟窝 , colobu.com/2015/11/17/Jax-RS-Performance-Comparison/在微服务流行的今天,我们会从纵向和横向分解代码的逻辑,将一些独立的无状态的代码单 ...

  9. Java开源——常见J2EE框架简介

    Java开源--常见J2EE框架简介 Spring Framework Spring是一个解决了许多在J2EE开发中常见的问题的强大框架. Spring提供了管理业务对象的一致方法并且鼓励了注入对接口 ...

最新文章

  1. 输入3个数a,b,c,要求按由小到大的顺序输出
  2. 宜信开源|数据库审核软件Themis的规则解析与部署攻略
  3. [转]PowerDesigner使用
  4. 【Python】Python字典的高级用法-统计计数
  5. AngularJS集合数据遍历显示
  6. java jsr_Java EE 7中包含哪些JSR?
  7. vue 父组件 调用 子组件的方法
  8. js微信监听返回_JS监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法...
  9. 在Ubuntu 18.04系统上安装和配置DBeaver的步骤
  10. C# JsonHelper
  11. 【个人笔记】OpenCV4 C++ 快速入门 11课
  12. 读书 -- 个人购书经验总结
  13. ! [rejected] master - master (fetch first)问题的解决方案
  14. 个人电脑php漏洞怎么修复,PHP版 6.0 漏洞 要怎么修复
  15. ORAN C平面 Section Type 7
  16. 【CVPR2018】Deep Mutual Learning
  17. 解决Intellij IDEA 一直在indexing,清除缓存后重启无效,手动清除缓存
  18. 实例讲解基于 React+Redux 的前端开发流程
  19. simpread-PCB 基本布线规范与设计原则
  20. 无线通信系统中的一些基本概念

热门文章

  1. css iphone安全底部
  2. 抖音小店无货源玩法,玩好这几点小店轻轻松松月入上万
  3. Windows命令-FINDSTR命令(重点)
  4. 智能嵌入式系统大作业(1)
  5. APIpace 月出月落和月相API
  6. 坤 坤为地 坤上坤下
  7. ABAP alv和smartforms的基础使用
  8. 双极性SPWM、单极性SPWM和单极倍频SPWM的仿真结果对比
  9. html网页设计实训原理,网页设计实习目的及意义
  10. 修改mysql结束符号