Domain driven design aggregateidentifier的分布式使用

Domain driven design aggregateidentifier的分布式使用,domain-driven-design,axon,Domain Driven Design,Axon,我想知道在另一个(extensipn)聚合中跨服务使用聚合标识符是否会被视为不好的做法,该聚合共享它们都围绕同一个可识别实体旋转 我目前遇到的问题是,我们希望将一些逻辑(如果您愿意的话,可以是有界上下文)拆分为与最初创建聚合的服务不同的服务 一般来说,这似乎是可行的,因为当我在第二个服务中发送命令时,它会被拾取并更新其状态。由于我可以使用EventSourcingHandler也可以使用在另一个服务中创建的事件来操纵其状态,因此我从第一个服务聚合应用的源获取状态信息 我担心快照机制会对我不利,但

我想知道在另一个(extensipn)聚合中跨服务使用聚合标识符是否会被视为不好的做法,该聚合共享它们都围绕同一个可识别实体旋转

我目前遇到的问题是,我们希望将一些逻辑(如果您愿意的话,可以是有界上下文)拆分为与最初创建聚合的服务不同的服务

一般来说,这似乎是可行的,因为当我在第二个服务中发送命令时,它会被拾取并更新其状态。由于我可以使用EventSourcingHandler也可以使用在另一个服务中创建的事件来操纵其状态,因此我从第一个服务聚合应用的源获取状态信息

我担心快照机制会对我不利,但显然,只要我确保聚合的“类型”名称不同,就可以单独存储快照

到目前为止,很好,对我来说唯一的一点是,第二个聚合没有(需要)初始构造函数CommandHandler,因为创建是在第一个聚合中完成的

那么,我是反对axon框架打算使用聚合的方式,还是这是一个可行的用例

@Aggregate
@Getter
@NoArgsConstructor
public class Foo {

  @AggregateIdentifier
  private String fooIdentifier;

  @CommandHandler
  public Foo(CreateFooCommand command) {
    apply(FooCreatedEvent.builder()
      .fooIdentifier(command.getFooIdentifier())
      .build());
  }

  @EventSourcingHandler
  public void on(FooCreatedEvent event) {
    this.fooIdentifier = event.getFooIdentifier();
  }

}


@Aggregate
@Getter
@NoArgsConstructor
public class Bar {

  @AggregateIdentifier
  private String fooIdentifier;

  private String barProperty;

  @CommandHandler
  public void on(UpdateBarCommand command) {

    apply(BarUpdatedEvent.builder()
      .fooIdentifier(this.fooIdentifier)
      .barProperty(command.getBarProperty())
      .build());
  }

  @EventSourcingHandler
  public void on(FooCreatedEvent event) {
    this.fooIdentifier = event.getFooIdentifier();
  }

  @EventSourcingHandler
  public void on(BarUpdatedEvent event) {
    this.barProperty = event.getBarProperty();
  }

}
我之所以尝试拆分,是因为我们希望将基本逻辑(聚合的创建,在本例中是一个载体)与发生的逻辑分离,并在不同的有界上下文中处理,以及分离的微服务(从和到施工现场的传输)。由于我无法两次发布同一聚合标识符但不同聚合类型的创建事件(构造函数中的CommandHandler,序列0),因此我无法完全分离这两种状态

所以我现在唯一的选择就是我上面介绍的,或者使用创建第二个聚合来设置一个不同的aggregateId,但也要在内部添加第一个聚合的aggregateId,以便发布事件时使用第一个聚合的aggregateId信息作为参考Id。要实现这一点,我必须在两个标识符之间保持一个映射,这看起来也不太好

提前感谢,,
Lars Karschen

您提出了一个非常有趣的解决方案Lars。不能说我曾经在一个服务创建聚合逻辑,而另一个服务加载相同的事件以自己的形式重新创建该状态的情况下拆分过聚合逻辑

那么,我是反对axon框架打算使用聚合的方式,还是这是一个可行的用例

@Aggregate
@Getter
@NoArgsConstructor
public class Foo {

  @AggregateIdentifier
  private String fooIdentifier;

  @CommandHandler
  public Foo(CreateFooCommand command) {
    apply(FooCreatedEvent.builder()
      .fooIdentifier(command.getFooIdentifier())
      .build());
  }

  @EventSourcingHandler
  public void on(FooCreatedEvent event) {
    this.fooIdentifier = event.getFooIdentifier();
  }

}


@Aggregate
@Getter
@NoArgsConstructor
public class Bar {

  @AggregateIdentifier
  private String fooIdentifier;

  private String barProperty;

  @CommandHandler
  public void on(UpdateBarCommand command) {

    apply(BarUpdatedEvent.builder()
      .fooIdentifier(this.fooIdentifier)
      .barProperty(command.getBarProperty())
      .build());
  }

  @EventSourcingHandler
  public void on(FooCreatedEvent event) {
    this.fooIdentifier = event.getFooIdentifier();
  }

  @EventSourcingHandler
  public void on(BarUpdatedEvent event) {
    this.barProperty = event.getBarProperty();
  }

}
老实说,我不认为这是预期用途。与其说是因为轴突,不如说是因为您使用的术语有界上下文。在不同的语境中,你应该有意识地分享,因为术语(普遍存在的语言)在不同的语境中是不同的。您的事件本质上是该语言的一部分,因此我通常不会建议您与其他服务共享整个聚合流

您所谈论的这些服务是否真正属于不同的有界上下文,我现在无法推断,因为我不是您的领域专家。如果它们确实属于同一个上下文,那么共享事件就完全可以了。然后我仍然不会基于相同的事件重新创建不同的聚合。因此,让我补充另一个可能有用的概念

我从你的描述中得到的是,你有一个叫做车辆集合的东西,它转换不同的状态。a难道不是你要寻找的解决方案吗?这样,您就可以拥有一个包含所有基本功能的父级
车辆
集合,并在必要时提供更具体的实现?尽管如此,这可能并不完全符合您的解决方案,根据您的描述,我不确定这一点

因此,我将添加第三个指针,我认为这是值得强调的:

由于我无法两次发布同一聚合标识符但不同聚合类型的创建事件(构造函数中的CommandHandler,序列0),因此我无法完全分离这两种状态

这一行表示您希望在不同的聚合之间重用聚合标识符,这也会出现在问题的标题中。正如您所注意到的,[聚合标识符,序列号]对需要是唯一的。因此,为不同类型的聚合重用聚合标识符不是一个选项。但是,要知道Axon将使用聚合标识符类的
toString
方法来填充聚合标识符字段。如果因此调整
toString()
方法以包含聚合类型,则可以保留唯一性要求并仍然重用聚合标识符

例如,包含
UUID
VehicleId
类的
toString
方法通常会输出以下内容:

  • 684ec9f4-b9f8-11ea-b3de-0242ac130004
但如果将
更改为字符串
以包含聚合类型,则会得到以下结果:

  • VehicleID[684ec9f4-b9f8-11ea-b3de-0242ac130004]

最后,我想分享三个要点:

  • Axon框架无意重用聚合流来重新创建不同的聚合类型
  • 多菌聚合可能是解决您的情况的一种方法
  • [aggregateId,seqNo]唯一性要求可以重用aggregateId,只要
    toString
    方法将聚合类型追加/前置到结果中

  • 我希望这对你的旅程有帮助,拉尔斯。如果您觉得有什么遗漏或我没有正确理解您的问题,请告诉我。

    您提出了非常有趣的解决方案Lars。不能说我曾经在一个服务创建聚合逻辑,而另一个服务加载相同的事件以自己的形式重新创建该状态的情况下拆分过聚合逻辑