事件源,使用 NEventStore 进行反腐败层设计

Event Source, Anti-Corruption layer design with NEventStore

我有两个具有一些相似功能的旧版企业应用程序。我需要构建一个系统来响应来自这些系统的数据更改事件、处理数据并通过 API 以多种格式公开组合结果。我想使用 Event Source/DDD 风格的架构,但我不确定它是否有意义。鉴于下面的简化模型,我该如何设计系统?

注意 - 已编辑以下内容以澄清问题:

两个系统都有基于日期包含不同价格的产品。当价格发生变化时,每个系统都可以发出一个事件 PriceChanged,其中包含遗留系统的标识符、该系统的主键、日期和新价格。产品 ID 对于系统是唯一的,但在两个系统之间可能不是唯一的,因此系统 ID 也将包括在内。

    PriceUpdated {
       int systemId,
       int productId,
       Date date,
       Float amount
    }

在我的新系统的限界上下文中,将有一个服务接收此事件并需要按 systemId、productId 和日期查找我的聚合,然后发出命令以更新聚合中的相应价格。我的聚合将定义为:

class ProductPriceAggregate
{
   Guid Id,
   int systemId,
   int productId,
   Date date,
   Float amount

   Apply(CreateProductPriceCommand e){
     Id = e.Id;
     systemId = e.systemId;
     productId = e.productId;
     date = e.date;
     RaiseEvent(new ProductPriceCreatedEvent(this))
   }

   Apply(UpdateProductPriceCommand d){
     amount = e.amount;
     RaiseEvent(new ProductPriceUpdatedEvent(this));
   }
}

如果我使用的是使用 GUID 存储流的 NEventStore,那么每个 aggreateId 都将由一个 GUID 表示。我的服务需要使用 systemId、productId 和日期来查询 GUID,以发出具有正确 ID 的命令。

该服务可能如下所示:

class PriceUpdateService : ISubscribeTo<PriceUpdated>{
  Handle<PriceUpdated>(PriceUpdated e)
  {
    var aggregateId = RetrieveId(e.systemId, e.productId, e.date);
      if (aggregateId == null)
        Raise(new CreateProductPriceCommand(e))
      else
        Raise(new UpdateProductPriceCommand(aggregateId, e.amount);
   }

   RetrieveId(int systemId, int productId, DateTime date)
   {
      // ???
   }
}

问题是查找聚合 ID 的最佳方法是什么?发出 PriceUpdated 事件的遗留系统将不知道这个新系统。我能否使用响应 ProductPriceCreatedEvent 更新的读取模型,其中包含足够的信息来查询 ID?我是否需要另一个负责索引 ProductPrices 的聚合?正如 VoiceOfUnreason 作为答案发布的那样,我可以使用可重复的约定通过 systemId、productId 和日期生成 ID。从 DDD 的角度来看,这是推荐的选项吗?

你控制自己的 ID 吗?

PriceUpdated {
   int systemId,
   int productId,
   Date date,
   Float amount
}

尝试查找 aggregateId 的替代方法是计算该 aggregateId 必须是什么。基本思想是,需要从这个事件中找到聚合的不同点共享一个封装计算的领域服务实例。

签名看起来像您在问题中写的查询

// Just a query, we aren't changing state anywhere.
var aggregateId = idGenerator.getId(e.systemId, e.productId, e.date);

任何给定的实现都采用它自己的 salt,您传递给它的参数,并生成一个散列,该散列是随处使用的通用 ID,用于将此参数组合映射到聚合。

当然,您可以通过传入具有不同盐的 idGenerator 来使用相同的事件数据为不同的聚合生成标识符。

对于您的 ID 为 UUID 的特定情况,您可以使用 Name-Based UUID