Domain driven design 如何在CQRS中建立银行转账模型

Domain driven design 如何在CQRS中建立银行转账模型,domain-driven-design,cqrs,event-sourcing,Domain Driven Design,Cqrs,Event Sourcing,我正在阅读,对在CQRS中实现它很好奇 我认为AccountingTransaction是一个聚合根,因为它保护不变量: 没有资金泄漏,应该从一个帐户转移到另一个帐户。 public class AccountingTransaction { private String sequence; private AccountId from; private AccountId to; private MonetaryAmount quantity; priv

我正在阅读,对在CQRS中实现它很好奇

我认为AccountingTransaction是一个聚合根,因为它保护不变量:

没有资金泄漏,应该从一个帐户转移到另一个帐户。

public class AccountingTransaction {
    private String sequence;
    private AccountId from;
    private AccountId to;
    private MonetaryAmount quantity;
    private DateTime whenCharged;

    public AccountingTransaction(...) {
        raise(new AccountingEntryBookedEvent(sequence, from, quantity.negate(),...);
        raise(new AccountingEntryBookedEvent(sequence, to, quantity,...);
    }
}
将AccountingTransaction添加到其存储库时。它发布多个AccountingEntryBookedEvent,用于更新查询端相应帐户的余额

每个db事务更新一个聚合根,最终的一致性,到目前为止还不错

但是,如果某些帐户应用转移约束,例如不能转移数量超过当前余额,该怎么办?我可以使用查询端获取帐户余额,但我担心来自查询端的数据过时了

public class TransferApplication {
    public void transfer(...) {
        AccountReadModel from = accountQuery.findBy(fromId);
        AccountReadModel to = accountQuery.findBy(toId);
        if (from.balance() > quantity) {
            //create txn
        }
    }
}
我应该在命令端对帐户建模吗?我必须为每个db事务更新至少三个聚合根(从/到帐户和帐户txn)


有些用例不允许最终的一致性。CQRS很好,但数据可能需要100%一致。CQR并不意味着/要求最终一致性


但是,事务/域模型存储将保持一致,并且该存储中的余额将保持一致,因为它表示当前状态。在这种情况下,不管查询端是否不一致,事务都应该失败。这将是一种有点奇怪的用户体验,尽管如此,100%一致的方法可能更好。

我记得其中的一些内容,但是M Fowler使用的事件含义与域事件不同。他使用了“错误”的术语,因为我们可以在他的“事件”定义中识别命令。所以基本上他说的是命令,而域事件是已经发生的事情,它永远不会改变

可能我没有完全理解Fowler所指的,但我会以不同的方式建模,更精确地尽可能接近领域。我们不能简单地提取一种模式,这种模式可以应用于任何金融应用程序,细微的细节可能会改变一个概念的含义

在OP的例子中,我认为我们可以有一个非显式的“交易”:我们需要一个借记金额的账户和另一个贷记金额相同的账户。我认为,最简单的方法是通过一个传奇故事来实现它

借记账户->借记账户->贷记账户->贷记账户->贷记账户=交易完成

这应该在几毫秒内发生,最多几秒钟,这将足以更新读取模型。人类和浏览器的速度比几秒钟还要慢。用户知道按F5或等待几分钟/小时。我不会太担心读取模型的准确性

如果事务是显式的,即域具有事务概念,并且业务确实存储了事务,那么情况就完全不同了。但即使在这种情况下,交易也可能由多个帐户id和一些金额以及一个完成标志来定义。然而,在这一点上继续下去是没有意义的,因为它实际上取决于域的定义和用例。

只有两个字:“保留模式”。
也许,但并非总是,您也可能需要“”模式。

修复了答案

最后,我的解决方案是将事务作为域模型


并将交易投影到AccountBalance,但我实施了特殊投影,以确保在发布实际事件之前所有数据的一致性。

是否希望bankapplication使用DDD?@Singh感谢您的评论。是的,CQRS的DDD。我有一个关于银行申请的项目。“你想要吗?”辛格我想看看,谢谢。你可以通过电子邮件发送(hippoom@gmail.com)如果你方便的话,你的答案在哪里(
public class TransferApplication {
    public void transfer(...) {
        Account from = accountRepository.findBy(fromId);
        Account to = accountRepository.findBy(toId);
        Transaction txn = new Transaction(from, to, quantity);
        //unit or work locks and updates all three aggregates
    }
}

public class AccountingTransaction {
    public AccountingTransaction(...) {
        if (from.permit(quantity) {
            from.debit(quantity);
            to.credit(quantity);
            raise(new TransactionCreatedEvent(sequence, from, to, quantity,...);
        }   
    }
}