一个优秀的CQRS框架Reveno
作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs
一、Reveno介绍
CQRS表示Command Query Responsibility Segregation,即命令和查询责任分离,是由Greg Young提出的一种将系统的读(查询)、写(命令)操作分离为两种独立子系统的架构模式。在一些应用场景下,这种分离是很有价值的,但要注意,CQRS对大多数系统而言,让系统变得更加复杂。
目前,大多数事务处理解决方案都经历了过度复杂的架构和难以维护的基础设施的痛苦,更不用说整体维护成本的昂贵。
Reveno是一个事件溯源的事务处理框架,提供了一个高性能、低延迟、支持容错、极其简单的一部事务处理、基于JVM的框架。Reveno的目标是提供一个简单易用的面向域的开发工具,透明的架构、非常适合的组件,使得性能最大化。Reveno可以每秒完成百万级的事务处理,而且延迟在微妙级。
下面以一个人工银行系统为例,遍历Reveno框架的整个开发,讲述了定义命令对象到执行事务处理和检查结果的整个过程。
二、定义事务处理模型
在Reveno中,有两种方法来定义模型——可变的或不可变的。下面的例子,我们使用了默认的方式——不可变的模型。首先,我们应该定义一个账户Account实体,它代表了一个银行客户:
public static class Account {
public final String name;
public final long balance;
public Account add(long amount) {
return new Account(name, balance + amount);
}
public Account(String name, long initialBalance) {
this.name = name;
this.balance = initialBalance;
}
}
由于Reveno框架采用了CQRS模式,我们需要为账户Account域类定义一个视图,它可用于查询模型:
public static class AccountView {
public final long id;
public final String name;
public final long balance;
public AccountView(long id, String name, long balance) {
this.id = id;
this.name = name;
this.balance = balance;
}
}
三、命令和事务处理操作
要执行任何事务处理,你需要执行命令。命令会根据一些内部的业务逻辑,派遣指定的事务处理执行,它实现了实际的状态突变。例子中简化了一些内容。Reveno有特定的DSL语法,它对于简单的案例非常有用,尤其是当命令句柄执行单个事务处理时。如果对延迟和吞吐量有较高的要求,推荐使用基本的API。
首先,我们定义一个带文件系统存储的新引擎实例。
Reveno reveno = new Engine("/tmp/reveno-sample");
接着定义一个从事务处理到查询模型的视图映射器:
reveno.domain().viewMapper(Account.class, AccountView.class, (id,e,r) -> new AccountView(id, e.name, e.balance));
现在,创建新的事务处理句柄,它把新账户添加到系统:
DynamicCommand createAccount = reveno.domain()
.transaction("createAccount",
(t, c) -> c.repo().store(t.id(), new Account(t.arg(), 0)))
.uniqueIdFor(Account.class).command();
快速说一下到底是怎么回事。我们创建了一个createAccount命令对象和它的句柄,添加了一个新账户到仓库中,并给新账户分配了新的ID(自动产生)账户ID可通过t.id()访问。
我们还需要定义一些事务处理借记/贷记账户的余额,出于简单性考虑,借记用负数表示。
DynamicCommand changeBalance = reveno.domain()
.transaction("changeBalance", (t, c) - >
c.repo().remap(t.longArg(), Account.class, (id, a) -> a.add(t.intArg("inc")))
)
.command();
一个changeBalance命令需要传递两个参数:一个是账户ID参数,另一个是资产的值参数。
四、事务处理的执行
第一个要执行的事务处理是创建一个新账户。但在这之前,必须启动引擎:
reveno.startup();
long accountId = reveno.executeSync(createAccount, map("name", "John"));
在添加10,000$到账户,比如:
reveno.executeSync(changeBalance, map("id", accountId, "inc", 10_000));
现在,事务处理模型有了单个账户实体,账户上的资产有10,000$。之后,可以访问查询模型,检查所有的事务处理是否做了相应的改变:
Assert.assertNotNull(reveno.query().find(AccountView.class, accountId));
Assert.assertEquals(reveno.query().find(AccountView.class, accountId).name, "John");
Assert.assertEquals(reveno.query().find(AccountView.class, accountId).balance, 10_000);
五、还原系统状态
在典型的事务处理环境中,每一个执行成功的结果北欧必须保存到物理存储设备,需要在耐用性和性能方面做最好的平衡,从而最大限度地减少在紧急情况下的损失,最大限度地提升系统 可用性和响应能力。
Reveno框架还比较年轻,但也比较稳定了。可以登录官网查看:http://reveno.org/