Dns DDD CQRS ES域对象现在暴露于表示层?

Dns DDD CQRS ES域对象现在暴露于表示层?,dns,domain-driven-design,cqrs,Dns,Domain Driven Design,Cqrs,我的问题很简单——在普通的DDD域中,对象不应该暴露于表示层(除非有些人喜欢它——我相信它是如何被称为裸对象的)。但是CQRS+ES呢?写入被委托给封装的算法(Events+Sagas),这些算法使用我相信的存储库,然后更新调用accept的对象(使用更改的字段)。所以有一些事务逻辑和任何与一致性强制相关的东西。。。我说得对吗?命令和查询位于域的顶部。您的表示层与此层对话 来自我的一位朋友: 命令通过显式地支持无处不在的语言 在系统边界捕获用户意图-考虑使用 案例。您可以将它们视为发送给您的用户

我的问题很简单——在普通的DDD域中,对象不应该暴露于表示层(除非有些人喜欢它——我相信它是如何被称为裸对象的)。但是CQRS+ES呢?写入被委托给封装的算法(Events+Sagas),这些算法使用我相信的存储库,然后更新调用accept的对象(使用更改的字段)。所以有一些事务逻辑和任何与一致性强制相关的东西。。。我说得对吗?

命令和查询位于域的顶部。您的表示层与此层对话

来自我的一位朋友:

命令通过显式地支持无处不在的语言 在系统边界捕获用户意图-考虑使用 案例。您可以将它们视为发送给您的用户的消息 域名。在这方面,它们也可以作为域上的一层- 将内部与外部分离,使您能够逐渐 在不破坏外部的情况下,从内部引入概念。这个 命令执行器为您提供了一个很好的管道,您可以利用它 集中安全性、性能指标、日志记录和会话 管理等等

命令代表一个用例

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 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
    }
}
公共类AccountBalanceQuery
{
公共字符串AccountNumber{get;set;}
}
公共类AccountReads:IHandle
{
公共AccountBalanceReadModel句柄(AccountBalanceQuery)
{
//执行一个或多个查询并组装AccountBalancer模型
}
}
或者(这就是事件发挥作用的地方)读取模型已经可以是您想要的格式,因为它是通过处理事件(由聚合引发)创建的

公共类AccountReads:IHandle
{
公共AccountBalanceReadModel句柄(AccountBalanceQuery)
{
//执行简单的查询,因为您的读取模型已经以您喜欢的方式存在
}
}
回顾一下如何使用事件创建读取模型,您会发现一些处理事件和创建读取模型的方法

public class AcocuntBalanceReadModelDenormalizer : IHandle<AmountWithdrawn>
{
    public void Handle(AmountWithdrawn @event) 
    {
          // Update your specialized read model here (AccountBalanceReadModel)
    }
}
公共类acocuntbalanceradodenormalizer:IHandle
{
公共无效句柄(在事件中撤回的数量)
{
//在此处更新您的专用读取模型(AccountBalancerReadModel)
}
}

请注意,事件通常以最终一致的方式处理。这样做的好处是系统的性能和稳定性

它看起来很棒,以非常自然的方式解决了很多问题。。。但为什么发明和使用它的人声称,它应该只用于我们应用程序中的核心限定上下文?例如,汇款系统。当你在做CRUD时,这不会带来任何附加值。
public class AcocuntBalanceReadModelDenormalizer : IHandle<AmountWithdrawn>
{
    public void Handle(AmountWithdrawn @event) 
    {
          // Update your specialized read model here (AccountBalanceReadModel)
    }
}