如何正确关联启动另一个控制器传奇的多个实例的控制器传奇?

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.

有两种方法可以解决由主传奇和不同发起者启动子传奇的问题。

  1. 使用两个不同的命令。

让两个不同的命令开始子传奇,比如 MySubProcessFromMainSagaCommandMySubProcessFromSomewhereElseCommand。一个 Saga 可以有多个 IAmStartedByMessages<>

  1. 延长MySubProcessCommand

MySubProcessCommand 中包含一些数据以表明它是来自主要传奇还是其他发起者。

任何一种方式都会给你足够的信息来存储 sub-saga 是如何开始的,例如 Data.WasInitatedByMainSaga。在 sub-saga 完成逻辑中检查这一点。如果为真,则执行 ReplyToOriginator() 以与原始 main saga 进行通信。如果不是,请跳过回复。