我正在研究 ES 和 CQRS 系統的 PoC。
我定義了以下類來表示表示正在處理的命令輸出的命令和事件
public class CreateEstateCommand extends Command {}
public class ChangeEstateOwnerCommand extends Command {}
public class EstateCreatedEvent extends DomainEvent {}
public class EstateOwnerChangedEvent extends DomainEvent {}
這些命令正在實作以下介面的類中處理
/**
* Specific command handlers define what logic should be carried out during handling a command of type C.
* Single command execution results in an outcome of domain event of type E
*/
public interface CommandHandler<C extends Command, E extends DomainEvent> {
E handleCommand(C command);
}
public class EstateCreatedCommandHandler implements CommandHandler<CreateEstateCommand, EstateCreatedEvent> {
@Override
public EstateCreatedEvent handleCommand(CreateEstateCommand command) { /***/ }
}
public class ChangeEstateOwnerCommandHandler implements CommandHandler<ChangeEstateOwnerCommand, EstateOwnerChangedEvent> {
@Override
public EstateOwnerChangedEvent handleCommand(ChangeEstateOwnerCommand command) { /***/ }
}
現在是我想使用這些特定處理程式的部分。命令處理的流程可以表示如下:
命令通過API進入系統,轉發給CommandServce類處理
public class CommandService {
private final EventService eventService;
private final CommandGateway commandGateway;
public void handleCommand(CreateEstateCommand command) {
EstateCreatedEvent event = commandGateway.handleCommand(command);
eventService.handleEvent(event);
}
public void handleCommand(ChangeEstateOwnerCommand command) {
EstateOwnerChangedEvent event = commandGateway.handleCommand(command);
eventService.handleEvent(event);
}
}
如您所見,handleCommand()對于提交的每個命令,這些方法都是重復的。這背后的原因是我在運行時選擇適當的處理程式實作時遇到的問題,具體取決于Command.commandType:
@Service
public class CommandGateway {
private final Map<String, CommandHandler<?, ?>> commandHandlers;
@Autowired
public CommandGateway(Map<String, CommandHandler<?, ?>> commandHandlers) {
this.commandHandlers = commandHandlers;
}
public EstateCreatedEvent handleCommand(CreateEstateCommand command) {
EstateCreatedCommandHandler handler = (EstateCreatedCommandHandler) commandHandlers.get(command.getCommandType());
return handler.handleCommand(command);
}
public EstateOwnerChangedEvent handleCommand(ChangeEstateOwnerCommand command) {
ChangeEstateOwnerCommandHandler handler = (ChangeEstateOwnerCommandHandler) commandHandlers.get(command.getCommandType());
return handler.handleCommand(command);
}
}
上面的片段是我無法泛化的部分。是否有可能實作CommandGateway類,因此CommandService可以如下所示:
public class CommandService {
public <C extends Command, E extends DomainEvent> void handleCommand(C command) {
E event = commandGateway.handleCommand(command);
}
}
并提供型別安全的物件?
uj5u.com熱心網友回復:
根本問題是映射,其值是通配符型別的,即實際上是無型別的,更具體地說,不是鍵入以與鍵對齊。
您已經通過信任注入的映射的條目消除了一些型別安全性,因此只需使用 raw 更進一步CommandHandler,它將接受任何命令,并使用未經檢查的強制轉換來獲得正確型別的回傳值:
@SuppressWarnings({"unchecked", "rawtypes"})
public <C extends Command, E extends DomainEvent> E handleCommand(C command) {
CommandHandler handler = commandHandlers.get(command.getCommandType());
return (E)handler.handleCommand(command);
}
@SuppressWarnings 添加,因此您的 IDE 和構建都不會抱怨。
雖然這可能看起來很殘酷,但您實際上并沒有失去任何型別安全性。當您像這樣輸入地圖時,這會丟失,不幸的是,鑒于地圖輸入不會將值型別系結到鍵型別,這是不可避免的。
uj5u.com熱心網友回復:
如果你做這樣的事情怎么辦:
static abstract class Command {
public abstract String getCommandType();
public abstract Class<? extends DomainEvent> type();
}
你的實作:
public class CreateEstateCommand extends Command {
@Override
public String getCommandType() {
return null; // whatever here
}
@Override
public Class<EstateCreatedEvent> type() {
return EstateCreatedEvent.class;
}
}
用法是:
public DomainEvent handleCommand(Command command) {
return command.type().cast(commandHandlers.get(command.getCommandType()));
}
uj5u.com熱心網友回復:
使用泛型實作這種端到端的困難在于我們將運行時型別決策與編譯時型別檢查混合在一起。您的設計接近于在運行時檢索處理程式的 MVC 設計。因此,在編譯時只能進行有限的型別安全檢查。
讓我們看一個流程:
- API 通過其輸入接收命令
- 您可能有一個特定的控制器方法,它知道命令及其輸入,并且可以構造其中之一
CreateEstateCommand或ChangeEstateOwnerCommand - 現在,在運行時我們必須訪問地圖以了解關聯的
CommandHandler. 現在,我們必須在運行時執行此操作的真正原因是,由于使用Map.
如果您在 中有以下方法CommandGateway,則永遠無法確定Command從服務類傳遞的實體是否真的是映射想要的特定子項CommandHandler。因此,它不會編譯。
public DomainEvent handleCommand( Command command ) {
CommandHandler<? extends Command, ? extends DomainEvent> cmdHandler = commandHandlers.get(command.getCommandType());
return cmdHandler.handleCommand( command );
}
因此,您必須取消端到端型別安全要求或為您提供CommandHandler實體的“工廠”樣式映射。省略后者意味著 API 方法一開始就知道CommandHandler它需要什么。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/370270.html
