CQRS 读取模型同步:在微服务之间"query"写入模型的正确方法



我正在尝试使用HTTP CQRS API来遵循每个微服务模式的每次读/写数据库。

以为例

资产微服务

写入模式:

  • AssetWriteDb(mssql(
  • 资产类别/表
public class Asset
{
public Guid AssetId {get;set;}
public Guid ContractId {get;set;} //reference to contract of other microservice
...
}

读取型号

  • AssetReadDb(mongodb(
  • AssetAggregate类/集合
public class AssetAggregate
{
public Guid AssetId {get;set;}
public Guid ContractId {get;get;}
public string ContractNumber {get;set;} //this comes from Contract Microservice
...
}

合同微服务

写入模型

  • ContractWriteDb(mssql(
  • 合同类别/表格
public class Contract
{
public Guid ContractId {get;set;}
public string ContractNumber {get;set;}
...
}

读取型号

  • ContractReadDb(mongodb(
  • ContractAggregate类/集合
public class ContractAggregate
{
public Guid ContractId {get;set;}
public string ContractNumber {get;set;}
public int AssetCount {get;set;} //this comes from Asset microservice
...
}

合同聚合同步事件处理程序示例:

public class ContractAggregateHandler :
IHandleMessage<ContractChangedEvent> // published from ContractWriteDb mssql repository
IHandleMessage<AssetChangedEvent> // published from AssetWriteDb mssql repository 
{

public async Task Handle(ContractChangedEvent message)
{
await _bus.Send(new RefreshContractAggregateCommand(message.ContractId));
}
public async Task Handle(AssetChangedEvent message)
{
//since the event contains only AssetId, I need to retrieve the data from the asset microservice. i have two options to obtain the contractId from asset microservice:
//call the AssetApi microservice reading the AssetAggregate collection (mongodb)
//var contractId = await _mediator.Send(new GetAssetContractIdQuery(message.AssetId);

//call the AssetApi microservice reading the Asset table (sqlserver)
//var contractId = await _mediator.Send(new GetAssetContractIdFromWriteDbQuery(message.AssetId);

await _bus.Send(new RefreshContractAggregateCommand(contractId));
}
}

遵循查询应始终查询读取模型和命令应始终读取和写入写入模型的规则,实现这一点的最佳实践是什么?

在第一种情况下(读取mongodb资产读取模型(,我认为这是错误的:AssetChanged事件来自AssetWriteDb(sql server(,查询读取模型是不安全的。此外,如果我基于其他聚合生成聚合,我应该侦听AssetAggregateRefreshedEvent,但这将在聚合生成之间创建无限循环,因为AssetAggregate需要调用ContractAggregateRefreshedEvent以确保这些操作永远不会结束。

在第二种情况下,(阅读sql资产写入模型(我认为这是最安全的,但我需要管理大量";错误的";因为他们没有遵循规则";查询必须从读取模型中获取数据";。这就是为什么为了避免错误,我需要用不同的结尾词来区分它们,比如";FromWriteDbQuery";

我显然不想评估第三种选择:直接从Contract Microservice 查询AssetWriteDb

注意:;"公共";api网关;保护";所有来自外部的内部微服务。api gataway公开了以正确方式查询mongodb的客户端所需的始终正确的查询。这个问题只是关于骨料的内部处理以及如何";查询";微服务之间的写入模型

注2:我没有写";"同步";业务逻辑(RefreshContractAggreagteHandler(,因为它只是一个";sql查询";向ContractWriteDb投影";ContractAggregate";,然后为了映射assetCount,我有同样的问题,我想从Contract Microservice查询AssetWriteDb,所以有完全相同的主要问题(

请记住,可以有任意多个读取模型,针对特定的查询进行优化。假设资产微服务中对合同ID的更改在AssetChangedEvent中捕获,那么您的合同服务可以维护自己的资产到合同ID的映射,作为其写入模型的一部分,然后当您需要解析给定资产的合同ID时,您可以检查该映射。尽管该映射具体是合同写入模型的一部分(它可能是合同mssql中的一个表(,但从概念上讲,它是资产的读取模型(事实上,AssetChangedEvents流是资产的读模型,这意味着事件处理程序已经在资产读模型池和合同写入模型池中至少有一个脚趾(。

AssetChangedEvent处理程序然后首先检查事件是否接触到合同ID;如果是,它将更新映射。

顺便说一句,这种微服务分解似乎是基于从关系模式中提取表,并使每个表都成为自己的微服务。这是通往许多额外复杂性的高速公路之一(尤其是如果您想要同步(。通常情况下,基于命令进行分解是一个更好的想法(尽管几乎每个从业者都必须学习"艰难的方法"(。如果资产和合同如此紧密地联系在一起,以至于对其中一个的更改往往会波及到对另一个的变更(尤其是在需要更强的一致性保证的情况下(,那么将它们放在同一个微服务中可能是最好的主意。

相关内容

最新更新