CQRS如何避免重复命令和事件之间的字段?

时间:2017-07-27 13:53:22

标签: java cqrs event-sourcing

我正在使用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上下文中进行。

6 个答案:

答案 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)

刚从评论中的讨论中汇总这个答案。

Compose, don't inherit

我肯定不会在这样的情况下使用继承,因为它只会增加不必要的复杂性,也没有继承那种行为。

另一种选择是为您的命令和事件制定明确的合同。那就是有两个接口 - IPostIEvent - 并在命令和事件中实现这些接口。

关于命名:我们都知道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> { ... }

如果由于某些原因需要区分命令和事件操作的属性,我只将这些特定的属性放在ConcreteCommandConcreteEvent类中。

这里的继承模型非常简单。除了扩展抽象类之外,您可能几乎不需要做任何事情,仅需执行常见的Action即可实现。并且在Action不需要属性的情况下,只需定义一个class EmptyAction implements Action实现即可在这些类型的命令和事件中使用。