我对如何将数据同步到查询数据库感到困惑。
假设我有一个聚合:CreditAccount和一些命令可能会产生CreditAccountBalanceChangedEvent:
public class CreditAccount extends AbstractAnnotatedAggregateRoot<Long> {
@AggregateIdentifier
private Long id;
private int balance;
private DateRange effectiveDateRange;
@CommandHandler
public CreditAccount(CreateCreditAccountCommand command) {
apply(new CreditAccountCreatedEvent(command.getAccountId(),
command.getEffectiveDateRange()));
apply(new CreditAccountBalanceChangedEvent(command.getAccountId(),
command.getAmount()));
}
@EventHandler
private void on(CreditAccountCreatedEvent event) {
this.id = event.getAccountId();
this.effectiveDateRange = event.getEffectiveDateRange();
}
@EventHandler
private void on(CreditAccountBalanceChangedEvent event) {
//notice this line, some domain logic here
this.balance = add(this.balance, event.getAmount());
}
private int add(int current, int amount) {
return current + amount;
}
}
public class CreditAccountBalanceChangedEvent {
private final long accountId;
private final int amount;
//omitted constructors and getters
}
在命令处理程序方面一切正常。然后我开始查询,但我发现我在这里写了一些重复的域逻辑:
@Transactional
@Slf4j
public class CreditAccountEventHandler {
private CreditAccountReadModelStore creditAccountReadModelStore;
@EventHandler
public void handle(CreditAccountCreatedEvent event) {
log.info("Received " + event);
creditAccountReadModelStore.store(accountDevriveFrom(event));
}
@EventHandler
public void handle(CreditAccountBalanceChangedEvent event) {
log.info("Received " + event);
final CreditAccountReadModel account = creditAccountReadModelStore
.findBy(event.getAccountId());
//notice this line, some domain logic here
account.setBalance(account.getBalance() + event.getAmount());
creditAccountReadModelStore.store(account);
}
//omitted setters and private methods
}
你可能注意到了,我在命令端和查询端都写了余额计算代码。我的问题是这在某些情况下是不可避免的还是我在错误的地方编写了域逻辑?
到目前为止,我的研究表明,事件代表了某些事情已经发生,因此它们中没有业务逻辑,它们只是数据持有者(但揭示了用户的意图)。那么我应该向CreditAccountBalanceChangedEvent添加一个“余额”字段并将余额计算代码移动到命令处理程序方法吗?
public class CreditAccount extends AbstractAnnotatedAggregateRoot<Long> {
//omitted fields
@CommandHandler
public CreditAccount(CreateCreditAccountCommand command) {
apply(new CreditAccountCreatedEvent(command.getAccountId(),
command.getEffectiveDateRange()));
apply(new CreditAccountBalanceChangedEvent(command.getAccountId(),
command.getAmount(), add(this.balance, command.getAmount())));
}
@EventHandler
private void on(CreditAccountBalanceChangedEvent event) {
//notice this line, some domain logic here
//event.getAmount() is no use here, just for auditing?
this.balance = event.getBalance();
}
}
在这种情况下,我可以使用 event.getBalance() 删除查询端的余额计算。
很抱歉出现全屏问题,任何想法都值得赞赏。