提问者:小点点

DDD和CQRS/ES不会打破DDD的持久性不可知性吗?


DDD中的Domain模型应该与持久性无关。

CQRS要求我为我的读模型中不想要的一切触发事件。(顺便说一下,把我的模型分成一个写模型和至少一个读模型)。

ES要求我为改变状态的一切触发事件,我的聚合根必须处理事件本身。

这对我来说似乎不是很坚持不可知论。

那么,如何将DDD和CQRS/ES结合起来,而不会对域模型造成严重影响呢?

读模型是否也在DDD域模型中?还是在它之外?

CQRS/ES事件是否与DDD域事件相同?

编辑:

我从答案中得到的是:

是的,ORM领域模型对象的实现将不同于使用ES。问题是错误的方法。首先编写领域模型对象,然后决定如何持久化(更多类似事件=

但我怀疑,你将永远能够使用ES(没有大的添加/更改你的领域对象),如果你没有做出这个决定,也使用ORM没有决定它前面会造成很大的痛苦。:-)


共3个答案

匿名用户

命令

归根结底,CQRS意味着你应该把你的阅读和写作分开。

通常,命令到达系统并由某种函数处理,然后返回该命令产生的零个、一个或多个事件:

handle : cmd:Command -> Event list

现在您有了一个事件列表。您所要做的就是将它们持久化在某个地方。执行此操作的函数可能如下所示:

persist : evt:Event -> unit

但是,这样的持久化函数纯粹是一个基础设施问题。客户端通常只会看到一个将命令作为输入而不返回任何内容的函数:

attempt : cmd:Command -> unit

其余的(句柄,后跟坚持)是异步处理的,因此客户端永远不会看到这些函数。

查询

给定一个事件列表,您可以重放它们以将它们聚合成所需的结果。这样的函数本质上看起来像这样:

query : target:'a -> events:Event list -> Result

给定事件列表和要查找的目标(例如ID),这样的函数可以将事件折叠成结果。

执着无知

这是否迫使您使用特定类型的持久性?

这些函数都不是根据任何特定的持久性技术定义的。您可以使用

  • 内存列表
  • 演员
  • 事件存储
  • 文件
  • 斑点
  • 数据库,甚至
  • 等等

从概念上讲,它确实迫使您从事件的角度考虑持久性,但这与ORM的方法没有什么不同,ORM迫使您从实体和关系的角度考虑持久性。

这里的重点是很容易将CQRSES体系结构与大多数实现细节分离。这通常是持久的——无知的。

匿名用户

你问题中的许多前提都是非常二进制/黑白的。我不认为DDD、CQRS或事件源是那么规范的——有许多可能的解释和实现。

也就是说,只有一个你的前提困扰着我(强调我的):

ES要求我为改变状态的一切触发事件,我的聚合根必须处理事件本身。

通常AR会发出事件——它们不会处理它们。

在任何情况下,CQRS和ES都可以实现为完全与持久性无关(通常也是)。事件存储为流,可以存储在关系数据库、非关系型数据库、文件系统、内存等中。事件的存储通常在应用程序的边界实现(我认为这是基础设施),领域模型不知道它们的流是如何存储的。

类似地,读模型可以存储在任何可以想象的存储介质中。您可以有10种不同的读模型和投影,每个模型和投影都存储在不同的数据库和不同的格式中。投影只处理/读取事件流,否则与域完全解耦。

没有比这更与持久性无关的了。

匿名用户

不知道这有多正统,但是我有一个当前的事件源实体模型做了这样的事情,这可能说明了区别 . . . (C#示例)

public interface IEventSourcedEntity<IEventTypeICanRespondTo>{
    void When(IEventTypeICanRespondTo event);
}

public interface IUser{
    bool IsLoggedIn{get;}
}

public class User : IUser, IEventSourcedEntity<IUserEvent>{
     public bool IsLoggedIn{get;private set;}

     public virtual void When(IUserEvent event){
           if(event is LoggedInEvent){
               IsLoggedIn = true;
           }
     }
}

非常简单的例子——但是你可以在这里看到事件的持久化方式(甚至是IF)是在域对象之外的。你可以通过存储库轻松做到这一点。同样,CQRS受到尊重,因为我读取值的方式与设置值的方式是分开的。例如,假设我有一个用户的多个设备,并且只希望它们在超过两个设备时登录?

public class MultiDeviceUser : IUser, IEventSourcedEntity<IUserEvent>{
     private IEnumerable<LoggedInEvent> _logInEvents = . . . 
     public bool IsLoggedIn{
         get{
              return _logInEvents.Count() > MIN_NUMBER_OF_LOGINS;
         }
     }

     public void When(IUserEvent ev){
          if(ev is LoggedInEvent){
             _logInEvents.Add(ev);
          }
     }
}

但是,对于调用代码,您的操作是相同的。

var ev = new LoggedInEvent();
user.When(ev);

if(user.IsLoggedIn) . . . .