我有一个控制器传奇,过去常常在一个事务中启动一个包含3个动作的进程。我现在正在将这个子流程重构为一个单独的传奇。这样做的结果是原始的saga将启动新的“sub-saga”的多个实例(这个sub-saga也将由其他非传奇进程启动,通过相同的命令)。我的问题是如何以最好的方式关联这种传奇的层次结构?
在下面的示例中,主要传奇将尝试使用相同的correlationId启动子传奇的三个实例。即使这样做,3个实例也会通过处理源自所有实例的“已完成事件”而相互干扰。
public class MyMainSaga : Saga<MyMainSagaData>,
IAmStartedByMessages<MyMainCommand>,
IHandleMessage<MySubProcessCommandCompletedEvent>
{
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<MyMainSagaData> mapper)
{
mapper.ConfigureMapping<MyMainCommandCompletedEvent>(message => message.CorrelationId).ToSaga(data => data.CorrelationId);
}
public void Handle(MyMainCommand message)
{
Data.CorrelationId = message.CorrelationId;
foreach (var item in message.ListOfObjectsToProcess)
{
Bus.Send(new MySubProcessCommand{
CorrelationId = Data.CorrelationId,
ObjectId = item.Id
});
}
}
public void Handle(MySubProcessCommandCompletedEvent message)
{
SetHandledStatus(message.ObjectId);
if(AllObjectsWhereProcessed())
MarkAsComplete();
}
}
public class MySubSaga : Saga<MySubSagaData>,
IAmStartedByMessages<MySubProcessCommand>,
IHandleMessage<Step1CommandCompletedEvent>,
IHandleMessage<Step2CommandCompletedEvent>,
IHandleMessage<Step3CommandCompletedEvent>
{
protected override voidConfigureHowToFindSaga(SagaPropertyMapper<MySubSagaData> mapper)
{
mapper.ConfigureMapping<Step1CommandCompletedEvent>(message => message.CorrelationId).ToSaga(data => data.CorrelationId);
mapper.ConfigureMapping<Step2CommandCompletedEvent>(message => message.CorrelationId).ToSaga(data => data.CorrelationId);
mapper.ConfigureMapping<Step3CommandCompletedEvent>(message => message.CorrelationId).ToSaga(data => data.CorrelationId);
}
public void Handle(MySubProcessCommand message)
{
Data.CorrelationId = message.CorrelationId;
Data.ObjectId = message.ObjectId;
Bus.Send(new Step1Command{
CorrelationId = Data.CorrelationId;
});
}
public void Handle(Step1CommandCompletedEvent message)
{
Bus.Send(new Step2Command{
CorrelationId = Data.CorrelationId;
});
}
public void Handle(Step2CommandCompletedEvent message)
{
Bus.Send(new Step3Command{
CorrelationId = Data.CorrelationId;
});
}
public void Handle(Step3CommandCompletedEvent message)
{
Bus.Publish<MySubProcessCommandCompletedEvent>(e => {
e.CorrelationId = Data.CorrelationId;
e.ObjectId = Data.ObjectId;
});
MarkAsComplete();
}
}
我看到的唯一解决方法是更改子传奇以生成单独的correlationId以及保持创建者ID。 E.g:
public void Handle(MySubProcessCommand message)
{
Data.CorrelationId = Guid.NewGuid();
Data.OriginatorCorrelationId = message.CorrelationId;
Data.ObjectId = message.ObjectId;
Bus.Send(new Step1Command{
CorrelationId = Data.CorrelationId;
});
}
public void Handle(Step1CommandCompletedEvent message)
{
Bus.Send(new Step2Command{
CorrelationId = Data.CorrelationId;
});
}
public void Handle(Step2CommandCompletedEvent message)
{
Bus.Send(new Step3Command{
CorrelationId = Data.CorrelationId;
});
}
public void Handle(Step3CommandCompletedEvent message)
{
Bus.Publish<MySubProcessCommandCompletedEvent>(e => {
e.CorrelationId = Data.OriginatorCorrelationId;
e.ObjectId = Data.ObjectId;
});
MarkAsComplete();
}
这个问题是否有“最佳实践”解决方案?我一直在考虑使用Bus.Reply,当子传奇完成时通知MainSaga。问题是另一个消费者也在不等待已完成的事件/回复的情况下发送MySubProcessCommand。
答案 0 :(得分:3)
最佳做法是在子传奇中使用ReplyToOriginator()
与主要传奇进行沟通。这种方法暴露在Saga基类上。
有两种方法可以解决主要传奇和不同发起者启动子传奇的问题。
让两个不同的命令启动子传奇,比如
MySubProcessFromMainSagaCommand
和MySubProcessFromSomewhereElseCommand
。对于Saga来说,可以有多个IAmStartedByMessages<>
。
MySubProcessCommand
在MySubProcessCommand
中包含一些数据,以表明它是来自主要传奇还是其他发起人。
无论哪种方式都会为您提供足够的信息来存储子传奇的启动方式,例如Data.WasInitatedByMainSaga
。在子传奇完成逻辑中检查这一点。如果是,请执行ReplyToOriginator()
与原始主传奇进行通信。如果没有,请跳过回复。