J'ai une saga de contrôleur qui avait l'habitude d'avoir une étape commençant un processus contenant 3 actions dans une transaction. Je suis en train de refactoriser ce sous-processus dans une saga séparée. Le résultat de ceci sera que la saga originale commencera plusieurs instances du nouveau "sous-saga" (cette sous-saga sera également commencée par d'autres processus non-saga, par la même commande). Mon problème est de savoir comment corréler de la meilleure façon possible cette hiérarchie des sagas?Comment corréler correctement une saga de contrôleur qui démarre plusieurs instances d'une autre saga de contrôleur?
Dans l'exemple suivant, la saga principale tentera de démarrer trois instances de la sous-saga avec le même ID de corrélation. Même si cela devait fonctionner, les 3 instances interfèreraient l'une avec l'autre en manipulant des "événements terminés" provenant de toutes les instances.
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();
}
}
La seule sollution que je vois est de changer la sous-saga pour générer une correlationId séparée ainsi que de garder l'identifiant du donneur d'ordre. 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();
}
Existe-t-il une solution «meilleure pratique» à ce problème? J'ai pensé à utiliser Bus.Reply, notifiant le MainSaga quand la sous-saga est terminée. Le problème avec ceci est qu'un autre consommateur envoie également MySubProcessCommand sans attendre un événement/réponse terminé.
Merci pour la réponse. Ensuite, la sous-saga devra créer son propre identifiant de corrélation, que je suppose être okei. Normalement, mes sagas utilisent l'identifiant de corrélation du message startedBy, qui semble maintenant faux, étant donné que ce type de cas existe. – sp1nakr
Dans notre cas, la réponse ne fonctionnera pas, car la dernière étape de la saga est un gestionnaire de messages gérant un message provenant d'un autre service. Le message de réponse sera donc dirigé vers ce point de terminaison et non vers l'extrémité de la saga principale. Une approche utilisant Bus.SendLocal() serait-elle conseillée? Sinon, je ne vois pas d'autre solution que la publication d'un "processus fini", ou similaire. – sp1nakr
Vous avez raison @ sp1nakr. :) La méthode correcte pour appeler est 'ReplyToOriginator()', pas 'Bus.Reply()'. J'ai mis à jour ma réponse. Le cas que vous décrivez est le même que https://docs.particular.net/nservicebus/sagas/reply-replaytooriginator-differences et https://docs.particular.net/nservicebus/sagas/#notifying-callers-of-status . – janovesk