我的问题很简单 - 在正常的DDD域中,对象不应该暴露给表示层(除了一些人实际上更喜欢它 - 裸体对象如何被称为我相信)。但是CQRS + ES怎么样?写入被委托给封装算法(Events + Sagas),它使用我相信的存储库,然后更新对象调用accept(使用更改的字段/字段)。所以有一些事务逻辑和任何与执行一致性相关的事情......我是对的吗?
答案 0 :(得分:2)
命令和查询位于您的域之上。您的表示层与此图层对话。
来自我的earlier blogpost:
命令可以帮助您明确地支持无处不在的语言 在系统边界捕获用户意图 - 考虑使用 案例。您可以将它们视为发送给您的邮件 域。在这方面,它们也可以作为您网域的一层 - 将内部与外部分离,让您逐渐消失 在不破坏外部的情况下在内部介绍概念。该 命令执行程序为您提供了一个可以利用的好管道 集中安全性,性能指标,日志记录,会话 管理等等。
命令代表一个用例。
public class WithdrawMoneyCommand
{
public WithDrawMoneyCommand(string account, decimal amount)
{
// Guard and assign here..
}
public string Account { get; private set; }
public decimal Amount { get; private set; }
}
命令由命令处理程序执行。这是您对聚合执行某些操作的地方。无论是调用方法,改变状态,还是播放和录制新事件。事务边界位于命令处理程序级别。
public class WithdrawMoneyCommandHandler : IHandle<WithdrawMoneyCommand>
{
public void Handle(WithdrawMoneyCommand command)
{
// Get your account here, and do something with it..
}
}
这是写方面。
在阅读方面,有人向您发送查询,然后您回复。就这么简单。
public class AccountBalanceReadModel
{
public string AccountNumber { get; set; }
public decimal Value { get; set; }
}
读取模型可以在运行时查询一个或多个聚合。
public class AccountBalanceQuery
{
public string AccountNumber { get; set; }
}
public class AccountReads : IHandle<AccountBalanceQuery, AccountBalanceReadModel>
{
public AccountBalanceReadModel Handle(AccountBalanceQuery query)
{
// Do query or queries and assemble an AccountBalanceReadModel
}
}
或(这是事件发挥作用的地方)读取模型已经可以采用您想要的格式,因为它是通过处理事件(由聚合引发)创建的。
public class AccountReads : IHandle<AccountBalanceQuery, AccountBalanceReadModel>
{
public AccountBalanceReadModel Handle(AccountBalanceQuery query)
{
// Do simple query because your read model is already there in the way you like it
}
}
回顾如何使用事件创建阅读模型,您可以处理事件并创建阅读模型。
public class AcocuntBalanceReadModelDenormalizer : IHandle<AmountWithdrawn>
{
public void Handle(AmountWithdrawn @event)
{
// Update your specialized read model here (AccountBalanceReadModel)
}
}
请注意,事件通常以最终一致的方式处理。这样做的好处是系统的性能和稳定性。