我无法理解将 DDD 与 CQRS 混合在一起的概念。
从表面上看,您无法完全理解 DDD 和 事件溯源 的组合。 CQRS 和事件溯源是不同的想法(恰好可以很好地结合在一起)。
Today Post 允许添加 10 条评论,并且方法 addCommnet() 保护该规则。但它并不总是那样。一年前,用户最多可以添加 20 条评论。所以应用过去的事件可能不符合当前规则。
这绝对是真的。但是请注意,如果您有一个包含 15 个 cmets 的 non 事件来源的帖子,并且您现在尝试制定一个“规则”,现在只允许 10 个 cmets,那么您仍然有一个问题。
我对这个难题(两种风格)的回答是,您需要对所涉及的职责有稍微不同的理解。
领域模型的职责是行为;它描述了可以从当前状态到达的状态。领域模型不应该限制你处于坏状态,它应该防止好的状态变成坏状态。
在版本一中,我们可以说Post 的状态包括TwentyList of Comments,其中 TwentyList 是(令人惊讶的)一个最多可以容纳 20 个评论标识符的容器。
在第二版中,我们希望保持 10 cmets 的限制,我们不会将 TwentyList 更改为 TenList,因为这给我们带来了向后兼容性问题。相反,我们将域规则更改为“不得将任何 cmets 添加到具有 10 个或更多 cmets 的帖子中”。数据schema不变,不良状态仍然可以表示,但允许的状态transitions受到很大限制。
具有讽刺意味的是,Greg Young 的Versioning in an Event Sourced System 是一本值得阅读以获取更多见解的好书。从高层次上讲,事件版本控制只是消息版本控制,状态只是先前模型为当前模型留下的消息。
值类型与规则约束无关,它们与语义约束有关。
请记住,时间线非常不同;行为是关于现在和下一个,但状态是关于过去。状态应该比行为更持久(这意味着对设计资本的相应投资)。
我的领域模型真的需要了解有关基础设施的任何信息(这是 ES 保存数据的方式)吗?
不,域模型不需要了解基础架构。
但事件不是基础设施——它们是状态。 AddComment 和 RemoveComment 事件的日志是状态,就像 Comment 条目的列表是状态一样。
“行为”的最一般形式是将当前状态作为输入并发出事件作为输出的函数
List<Event> act(State currentState);
因为我们总是可以在外层,获取事件(这是状态的非破坏性表示,并从中构建状态。
State act(State currentState) {
List<Event> changes = act(currentState)
State nextState = currentState.apply(changes)
return nextState
}
List<Event> act(List<Event> history) {
State initialState = new State();
State currentState = initialState.apply(changes)
return act(currentState)
}
State act(List<Event> history) {
// Writing this out long hand to drive home the point
// we could of course call act: List<Event> -> State
// to avoid duplication.
List<Event> changes = act(history)
State initialState = new State()
State currentState = initialState.apply(history)
State nextState = currentState.apply(changes)
return nextState;
}
关键是您可以在最一般的情况下实现行为,添加一些适配器,然后让管道选择最合适的实现。
再次强调,责任分离是您的指路明灯:管理现状的状态、管理允许更改的行为以及管道/基础设施都是不同的关注点。
用最简单的话来说:我正在寻找机会从我的聚合中删除许多 applyXXX() (或类似的具有重载方法的语言)方法
applyXXX 只是一个函数,它接受State 和Event 作为参数并返回一个新的State。您可以使用任何您想要的拼写和范围。