如何正确关联启动另一个控制器传奇的多个实例的控制器传奇?
How to correctly correlate a controller saga which starts multiple instances of another controller saga?
我有一个控制器 saga,它曾经有一个步骤来启动一个进程,该进程在一个事务中包含 3 个操作。我现在正在将这个子流程重构为一个单独的传奇。这样做的结果是原始 saga 将启动新 "sub-saga" 的多个实例(此子 saga 也将由其他非 saga 进程通过相同的命令启动)。我的问题是如何以最好的方式关联这个传奇的层次结构?
在下面的示例中,主saga 将尝试启动具有相同correlationId 的sub-saga 的三个实例。即使这行得通,这 3 个实例也会通过处理来自所有实例的 "completed events" 来相互干扰。
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。例如:
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();
}
这个问题有"best practice"解决方案吗?我一直在考虑使用 Bus.Reply,在子传奇完成时通知 MainSaga。问题是另一个消费者也在发送 MySubProcessCommand 而没有等待完成 event/reply.
最佳做法是在子传奇中使用 ReplyToOriginator()
与主传奇进行通信。此方法在Saga base上公开class.
有两种方法可以解决由主传奇和不同发起者启动子传奇的问题。
- 使用两个不同的命令。
让两个不同的命令开始子传奇,比如
MySubProcessFromMainSagaCommand
和 MySubProcessFromSomewhereElseCommand
。一个 Saga 可以有多个 IAmStartedByMessages<>
。
- 延长
MySubProcessCommand
在 MySubProcessCommand
中包含一些数据以表明它是来自主要传奇还是其他发起者。
任何一种方式都会给你足够的信息来存储 sub-saga 是如何开始的,例如 Data.WasInitatedByMainSaga
。在 sub-saga 完成逻辑中检查这一点。如果为真,则执行 ReplyToOriginator()
以与原始 main saga 进行通信。如果不是,请跳过回复。
我有一个控制器 saga,它曾经有一个步骤来启动一个进程,该进程在一个事务中包含 3 个操作。我现在正在将这个子流程重构为一个单独的传奇。这样做的结果是原始 saga 将启动新 "sub-saga" 的多个实例(此子 saga 也将由其他非 saga 进程通过相同的命令启动)。我的问题是如何以最好的方式关联这个传奇的层次结构?
在下面的示例中,主saga 将尝试启动具有相同correlationId 的sub-saga 的三个实例。即使这行得通,这 3 个实例也会通过处理来自所有实例的 "completed events" 来相互干扰。
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。例如:
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();
}
这个问题有"best practice"解决方案吗?我一直在考虑使用 Bus.Reply,在子传奇完成时通知 MainSaga。问题是另一个消费者也在发送 MySubProcessCommand 而没有等待完成 event/reply.
最佳做法是在子传奇中使用 ReplyToOriginator()
与主传奇进行通信。此方法在Saga base上公开class.
有两种方法可以解决由主传奇和不同发起者启动子传奇的问题。
- 使用两个不同的命令。
让两个不同的命令开始子传奇,比如
MySubProcessFromMainSagaCommand
和 MySubProcessFromSomewhereElseCommand
。一个 Saga 可以有多个 IAmStartedByMessages<>
。
- 延长
MySubProcessCommand
在 MySubProcessCommand
中包含一些数据以表明它是来自主要传奇还是其他发起者。
任何一种方式都会给你足够的信息来存储 sub-saga 是如何开始的,例如 Data.WasInitatedByMainSaga
。在 sub-saga 完成逻辑中检查这一点。如果为真,则执行 ReplyToOriginator()
以与原始 main saga 进行通信。如果不是,请跳过回复。