基於spring來實現crqs的一些問題

wilsonp發表於2014-03-16
初學CQRS架構,想在新專案中使用CQRS來進行架構,採用的是java來進行開發,架構採用傳統的springMvc+Jpa(Hibernate實現)+Spring4.0+Mysql,暫時沒有考慮EventSource的問題,因為事件溯源暫時還有些無法理解。
1、命令匯流排(CommandBus)實現:
命令匯流排使用一個Collection來儲存所有的命令執行器(ICommandExecutor),在Spring啟動的時候透過IOC進行載入注入,當命令進入的時候就去匯流排中透過反射查詢相應的Executor並向後執行(比如呼叫Domain或DomainService),程式碼如下:
@[author]Component[/author]
public class DefaultCommandBus implements ICommandBus<ICommandExecutor<ICommand>>{
@[author]Autowired[/author]
private List<ICommandExecutor> executors;
public DefaultCommandBus() {}
//執行命令
public void send(ICommand command) {
ICommandExecutor<ICommand> executor = findCommandExecutor(command);
executor.execute(command);
}
//在所有的命令執行器集合中查詢符合條件的執行器
private ICommandExecutor<ICommand> findCommandExecutor(ICommand command){
for (ICommandExecutor<ICommand> ce : executors) {
CommandListener ric = ce.getClass().getAnnotation(CommandListener.class);
Class<? extends ICommand> clazz = ric.commandClass();
if(clazz.getName().equals(command.getClass().getName())){
return ce;
}
}
return null;
}
}
2、事件聚合器(IEventAggragete)實現:(暫時沒支援非同步事件)
採用了和命令匯流排差不多的實現方法。
但由於事件和事件處理器是一對多的關係,所以這裡採用了Map來實現,Key為指定事件的名稱,Values為一個List<IEventHandler>,當Domain或DomainService使用聚合器Pulish一個訊息的時候,就去聚合器中查詢相應事件對應的一組事件處理器,並迴圈執行,程式碼如下:
@[author]Component[/author]
public class EventAggregate implements IEventAggregate {
//實現執行結果回撥
private ICallBack callBack;
//註冊所有的事件及對應的監聽器組
private Map<String,List<IEventHandler<IEvent>>> handlers = new HashMap<String, List<IEventHandler<IEvent>>>();

@[author]Autowired[/author]
private List<IEventHandler> eventHandlers;

//自動訂閱事件監聽器
@PostConstruct
public void autoSubcribe() {
for (IEventHandler<IEvent> eventHandler : eventHandlers) {
EventListener eventAnno = eventHandler.getClass().getAnnotation(EventListener.class);
if(eventAnno != null){
String eventTypeName = eventAnno.eventClass().getName();//獲取該handler感興趣的事件類的名稱
List<IEventHandler<IEvent>> ehs = findEventType(eventTypeName);//在聚合中查詢是否有該事件的處理器已經註冊
if(ehs != null){//如果存在
ehs.add(eventHandler);
handlers.put(eventTypeName, ehs);
}else{
ehs = new ArrayList<IEventHandler<IEvent>>();
ehs.add(eventHandler);
handlers.put(eventTypeName, ehs);
}
}
}
}
private List<IEventHandler<IEvent>> findEventType(String eventTypeName){
if(handlers.size() != 0){
return handlers.get(eventTypeName);
}
return null;
}
//訂閱事件
public <TEvent extends IEvent> void subcribe(IEventHandler<IEvent> handler) {
}
//解約事件處理器
public <TEvent extends IEvent> void desubcribe(IEventHandler<IEvent> handler) {
}
//領域物件釋出訊息
public <TEvent extends IEvent> void publish(TEvent event) {
String eventTypeName = event.getClass().getName();
List<IEventHandler<IEvent>> ehs = findEventType(eventTypeName);
if(ehs != null){
for (IEventHandler<IEvent> iEventHandler : ehs) {
iEventHandler.handle(event);
if(callBack != null){
callBack.callBack();
}
}
}
}
public void setCallBack(ICallBack callBack) {
this.callBack = callBack;
}
}
支援回撥,如果需要進行回撥,則需要注入ICallback介面的實現類(一般是實現了ICallback介面的Domain或DominService物件)
以上兩點就是CQRS的Command部分的核心實現了,但總覺得有些問題:
1、SpringMVC的前端控制器是DispatherServlet,其實也是servlet,所以必須基於容器來接受請求,當客戶端請求進來之後,容器(比如Jetty)會從執行緒池中調取一個空閒的執行緒來為該客戶端服務,這就保證了每個客戶端都有一個指定的執行緒來處理,即不存在資源競爭的問題(Spring採用了大量的ThreadLocal來保證了執行緒安全),那是否還需要採用類似Disruptor這種框架來封裝命令或事件的派發,因為Disruptor框架也是用來解決安全的傳送訊息問題的框架;
2、領域物件和領域服務會顯示的和事件聚合器發生耦合。(這裡如果採用Disruptor這類框架來封裝訊息的傳送,比如封裝為靜態方法,這才會出現資源競爭的問題,但只有這樣才能和Domain或DomainService解耦)
不知道大家對以上兩點的看法是怎麼樣的?效能上的問題還未考慮,如果只是單執行緒沒有資源競爭的情況下,效能應該沒有太大問題(一個類似於Tomcat或Jetty的web伺服器400左右的併發應該不錯了,畢竟是Http的短連線)

[該貼被wilsonp於2014-03-16 20:40修改過]

[該貼被wilsonp於2014-03-16 20:42修改過]

[該貼被wilsonp於2014-03-16 21:51修改過]

[該貼被wilsonp於2014-03-16 21:52修改過]

相關文章