我正在使用CQRS和事件采购实施项目。我意识到我的命令和事件几乎总是一样的。
我们说我有一个命令CreatePost
:
public class CreatePost implements Command {
private final String title;
private final String content;
}
从此命令触发的事件是相同的:
public class PostCreated implements Event {
private final String title;
private final String content;
}
你如何在你的应用程序中处理它?</ p>
编辑:我当然知道基本的OOP技术。我可以创建一个具有公共字段的抽象,但是这个问题需要在CQRS / ES上下文中进行。
答案 0 :(得分:6)
如何避免在命令和事件之间重复字段?
我不会 - 直到我完全无法忍受它。
从根本上说,命令和事件aren't objects,它们是消息 - 跨越边界的状态表示。我认为你的记忆表现不容忽视这一点非常重要。
消息模式的一个特征是它们随着时间的推移而发展,因此您需要了解compatibility。而且这里是踢球者:事件和命令在不同的时间尺度上发展。
命令消息是域模型与其他进程通信的方式;对API部分的更改是由公开/弃用功能驱动的。
但是在事件源世界中,事件是从以前版本的域到当前版本的消息。它们是我们部署新模型所需支持的一部分,这些模型可以从旧模型中断的地方恢复工作。
所以我会让命令和事件彼此分开 - 它们是不同的东西。
如果你在字段中看到很多重复,那么可能暗示某些价值类型尚未明确显示。
CreatePost
{ Post
{ Title
, Contents
}
}
PostCreated
{ Post
{ Title
, Contents
}
}
答案 1 :(得分:1)
只需为您的Post
实施一个模型,即:
public class PostModel {
private String title;
private String content;
// Add get/set methods
}
然后在您的事件和命令中重复使用它。
答案 2 :(得分:1)
如何避免在命令和事件之间重复字段?
请不要。依赖的成本+错误共同的风险高于维护收益。你可以忍受这种重复,就像你今天可能在你的领域模型,视图模型,查询模型等之间存在重复一样。
答案 3 :(得分:0)
只要它只是一个实现细节,您就可以使用任何您想要的。
在PHP
中,我使用了很多traits
来实现这种可重用性。您甚至可以使用继承,但客户端(使用这些类的代码)不应该依赖于基类;如果他们甚至没有发现你的事件和命令类共享某些东西,但我没有足够的Java经验来告诉你如何去做,那将是最好的。
P.S。我不打算创建接口,如上所述,这应该只是一个实现细节。
答案 4 :(得分:0)
刚从评论中的讨论中汇总这个答案。
我肯定不会在这样的情况下使用继承,因为它只会增加不必要的复杂性,也没有继承那种行为。
另一种选择是为您的命令和事件制定明确的合同。那就是有两个接口 - IPost
和IEvent
- 并在命令和事件中实现这些接口。
关于命名:我们都知道naming is hard,因此您应该根据您的业务或技术语言/词汇要求明智地选择名称。
因为命令通常携带其处理程序所需的更多信息,而不是事件处理程序所携带的事件,所以事件处理程序应保持尽可能薄。最好只携带所需的有效载荷。
分离命令和事件是必须的,因为命令代表现在发生的操作,而事件代表过去中发生的操作。它们通常可能是一个命令的结果,从外部世界 - 从有限的上下文的角度 - 指出在你当前的BC内发生的事情。
答案 5 :(得分:0)
我已经碰到了这一点,并且几乎在所有情况下,我都没有发现事件需要与特定域操作的命令具有不同属性的情况。我绝对发现属性获取器/等于/ hashCode / toString的简单复制/粘贴重复非常令人讨厌。如果可以返回,我先定义一个标记interface Action
,然后
interface Command<T extends Action> {
T getAction();
// other properties common to commands of all action types...
}
class AbstractCommand<T extends Action> implements Command<T> {
public T getAction() { ... }
// other properties...
}
interface Event<T extends Action> {
T getAction();
// other properties common to events of all action types...
}
class AbstractEvent<T extends Action> implements Event<T> {
public T getAction() { ... }
// other properties...
}
然后为每个域操作定义具体的实现。
class ConcreteAction implements Action {
// properties COMMON to the command and event(s)...
}
class ConcreteCommand extends AbstractCommand<ConcreteAction> { ... }
class ConcreteEvent extends AbstractEvent<ConcreteAction> { ... }
如果由于某些原因需要区分命令和事件操作的属性,我只将这些特定的属性放在ConcreteCommand
或ConcreteEvent
类中。
这里的继承模型非常简单。除了扩展抽象类之外,您可能几乎不需要做任何事情,仅需执行常见的Action
即可实现。并且在Action
不需要属性的情况下,只需定义一个class EmptyAction implements Action
实现即可在这些类型的命令和事件中使用。