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

本文关键字:反腐败 使用 NEventStore 事件源 | 更新日期: 2023-09-27 17:57:43

我有两个遗留的企业应用程序,它们具有一些类似的功能。我需要构建一个系统来响应来自这些系统的数据更改事件,处理这些数据,并通过多种格式的API公开组合结果。我想使用事件源/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的汇总吗?正如VoiceOfUnrealson发布的答案一样,我可以使用一个可重复的约定来根据systemId、productId和日期生成ID。从DDD的角度来看,这是推荐的选项吗?

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

您控制自己的ID吗?

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

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

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

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

任何给定的实现都有自己的salt,即传递给它的参数,并生成一个散列,该散列是用于将参数组合映射到聚合的通用id。

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

对于ID为UUID的特定情况,可以使用基于名称的UUID。