基于EDA和CQRS的拍卖案例

Xtext/Sculptor框架类似JdonFramework,提供一个事件驱动特性,他们项目组基于Sculptor开发了EDA事件驱动架构 CQRS模式的拍卖案例。

在线拍卖系统,接受大量的拍卖,简单模型如下(看懂下面代码需要DDD基础):


ValueObject Bet {
String betOfferId
String customerId
Double amount

Repository BetRepository {
save;
}
}

Service BettingEngine {
inject @BetRepository

placeBet(@Bet bet);
}

BettingEngine 处理这些拍卖然后存储信息。如果存在下面需求:
客户A已经完成多少拍卖?
哪些客户出了最高价?
出价最高的前10名有哪些?

当然,普通开发者会认为很容易,只要基于拍卖引擎开发多个查询即可,但是问题来了:
1.性能差:领域模型并没有为各种查询优化。
2.不具有可伸缩性:单个集中式数据库将成为瓶颈。
3.难于改变:在一个单一系统中融入太多功能。

CQRS(Command-Query Responsibility Segregation)能够将命令(修改数据)和查询(读取数据)
分离,将报表查询和业务领域处理保存更新数据两种职责分离。Command处理的结果将通过事件发送到查询子系统.

当一个拍卖发生后,我们发送领域事件Domain events。


DomainEvent BetPlaced {
- Bet bet
}

Service BettingPublisher {
publishEvent(@BetPlaced betEvent) publish to jms:topic:bet; //发送消息
}

以上代码是command这边实现,下面来看看Query查询这里实现,当然要接受事件了。
首先,我们要看看查询功能实现,假设有一个客户来查询他的拍卖信息:


Module customer {
Consumer BettingConsumer {
inject @CustomerStatisticsRepository
subscribe to jms:topic:bet //注意接受JMS消息
}

Service BettingQueryService {
getHighBetters => CustomerStatisticsRepository.findHighAverageCustomers;
}

Entity CustomerStatistics {
gap
String customerId key
int numberOfBets
double averageAmount index

Repository CustomerStatisticsRepository {
findByKey;
save;
List<@CustomerStatistics> findHighAverageCustomers(double limit);
protected findByCondition;
}
}
}

Query这边接受到Command那里发出已经进行拍卖的事件以后,就直接可以到仓储中查询,获取新的纪录。


本案例源代码下载
源码是以MongoDB为持久层,使用ActiveMQ 作为事件中线。

[该贴被admin于2010-09-13 09:04修改过]

由此案例可见,CQRS实现起来非常简单,就是将查询和修改分离,之间使用事件或消息来通讯。

Jdon框架的批量分页查询和模型增删改查本身就是CQRS,他们两者事件是通过缓存清除方式发生通讯,当然具体实现时,也可以使用JF的Domain events来实现修改和查询的通信,也就是,领域模型一旦发生修改,发出事件告知查询报表系统,激活一些功能。

这样的系统比较具有可伸缩性,查询功能和修改分离,查询可以使用分布式缓存或NoSQL如MongoDB这样的系统实现可伸缩,修改只在一台机器上实现,修改完成,通过事件总线,通知所有实现查询的主机。

这套方案非常可靠实用,据我个人了解,在很多大型BOSS(业务支撑)架构中都是这么做的,而且都已经运行,只不过他们没有上升到这个高度,而修改不是针对领域模型,是针对数据库的。

相关文章:
CQRS模式要好于MVC模型


[该贴被banq于2010-09-10 10:26修改过]


Repository BetRepository {
save;
}

这个是不是应该放在bet的外面?
[该贴被cleanearth于2010-09-10 12:38修改过]

我晕。。。。blogspot。。。。。。烦死了。。。我不会翻呃。。。。。。

如果客户端是 富客户端的话,组织起来会特别好。 而且客户端可以直接用Object去组织所有的对象。 尝试过这种方式,确实不错,但是不太建议一个人去开发。