Routing Slip in a long-running process

353 views
Skip to first unread message

Kenneth Avner

unread,
May 30, 2016, 8:20:21 AM5/30/16
to masstransit-discuss
I'm implementing a case-flow where the different steps in the case are Activities on a routing slip. The activities are created based on the initial message contents.

At some point an Activity will need to publish a message to trigger a user interaction in the UI. This may be picked up by a user seconds, or even hours later.

I cannot find an example showing how to solve this in an activity. I could use an async/await solution with a request-client in the activity - but are there any better solutions? I could very much accept this to be a solution if it wasn't for the potential wait time involved... Also, I would not like to divide the process into several routing slips. It should remain as a whole.

The alternative to all this, as I see it, would be to skip the whole Courier framework and just build a solution for with a "dynamic" state machine built up by own components. But I was hoping I didn't need that, as I feel the Courier was created for this purpose.. Am I on the wrong track here?

Kenneth Avner

unread,
May 30, 2016, 8:36:09 AM5/30/16
to masstransit-discuss
PS: The itinerary is created based on the initial message contents... ;) The activities are prewritten and configurable ofcourse.

Chris Patterson

unread,
May 30, 2016, 10:16:27 AM5/30/16
to masstrans...@googlegroups.com
So this use case is exactly why Courier was created. The ability to dynamically build an execution path through a distributed system. So you're definitely on the right track.

In the case of human (or even system) intervention, however, you need to use a combination of tools to make it flow together nicely.

Creating a state machine, using Automatonymous, would allow the routing slip to run to a certain point, and then terminate (either early, or at the point in which interaction is required), using business events to advance a state machine to the point of interaction. Then, the state is visible in the table, and the external system (person, or whatever) can trigger an event signaling that the interaction has completed, along with any new data that's been added (such as a confirmation/authorization number, or even an approver name). The state machine, upon observing the event, would then send a command to execute any remaining steps of the routing slip.

This does require some state to be maintained if the individual activities require it, however. What we've done is have an initial activity that gets added in both execution paths, which parses out the original document and revises the itinerary based on the document content. This way, we can reuse many of the same activities by having the document loaded from the initial receipt, or from durable storage in the event an interaction was required. We have used MongoDB or similar storage systems to keep around original documents in this way.

By using the state machine and events, we're able to well handle the case where two approvers might try to approve the document at the same time, since the approved event would no longer be accepted by the state machine if it's already been approved. You can also combine this with Quartz and MassTransit scheduling to be able to schedule a time at which if the document hasn't been approved, perhaps it gets escalated or alerted in some way to avoid being missed by an approver who may be out of the office.

Hopefully this gives you some ideas of where to go next!


On Mon, May 30, 2016 at 5:36 AM, Kenneth Avner <ken...@avner.no> wrote:
PS: The itinerary is created based on the initial message contents... ;) The activities are prewritten and configurable ofcourse.

--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-dis...@googlegroups.com.
To post to this group, send email to masstrans...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/masstransit-discuss/a2dac348-133f-49f2-8be7-0332a819dc82%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Kenneth Avner

unread,
May 30, 2016, 3:15:54 PM5/30/16
to masstransit-discuss
Thank you,

It certainly gives me som pointers. But I'm a afraid my experience with the framework until now is too limited to land the ball gracefully..

To be specific, my intuition would point me to trying this first;
  • Make the itinerary with activities as usual, then try to add an address not consumed by a activity - but a state machine instead, handling the external system/user and consuming the eventual business event. Not sure if this is possible, but please give me a bit more specific path if I am lost :)
  • Then, well, resuming the flow in the itinerary as normal. How? Well, that's unclear for me as well. I guess I AM lost, and my intuition failed here.
I had a look at the ReviseItinerary on the ExecuteContext... But sorry, I'm just randomly fumbling at this point.

What I extracted from your response was something like this;
  • Create a routing slip with activities leading up to the point where interaction is needed
  • End the routing slip here. Track it in a state machine (as the Courier example RoutingSlipStateMachine), and when the routing slip ends, handle the interaction in the state machine, triggering user intaraction and receiving the business event from the user interaction
  • When the interaction is finished, kick off a new routing slip with the next activities.
  • Then, when I hit another need for user interaction, do the same thing again.. 
Is this the way? Sort of?

What I sort of dreamt about was having a "special case" activity base class that made all of this trivial, but if I understand you correctly, that is not currently possible. If you do have time to hand-feed me just a bit more, I guess I would be able to do this the trial-and-error way.. Hopefully. Thanks again :)

-KA

mandag 30. mai 2016 16.16.27 UTC+2 skrev Chris Patterson følgende:
So this use case is exactly why Courier was created. The ability to dynamically build an execution path through a distributed system. So you're definitely on the right track.

In the case of human (or even system) intervention, however, you need to use a combination of tools to make it flow together nicely.

Creating a state machine, using Automatonymous, would allow the routing slip to run to a certain point, and then terminate (either early, or at the point in which interaction is required), using business events to advance a state machine to the point of interaction. Then, the state is visible in the table, and the external system (person, or whatever) can trigger an event signaling that the interaction has completed, along with any new data that's been added (such as a confirmation/authorization number, or even an approver name). The state machine, upon observing the event, would then send a command to execute any remaining steps of the routing slip.

This does require some state to be maintained if the individual activities require it, however. What we've done is have an initial activity that gets added in both execution paths, which parses out the original document and revises the itinerary based on the document content. This way, we can reuse many of the same activities by having the document loaded from the initial receipt, or from durable storage in the event an interaction was required. We have used MongoDB or similar storage systems to keep around original documents in this way.

By using the state machine and events, we're able to well handle the case where two approvers might try to approve the document at the same time, since the approved event would no longer be accepted by the state machine if it's already been approved. You can also combine this with Quartz and MassTransit scheduling to be able to schedule a time at which if the document hasn't been approved, perhaps it gets escalated or alerted in some way to avoid being missed by an approver who may be out of the office.

Hopefully this gives you some ideas of where to go next!

On Mon, May 30, 2016 at 5:36 AM, Kenneth Avner <ken...@avner.no> wrote:
PS: The itinerary is created based on the initial message contents... ;) The activities are prewritten and configurable ofcourse.

--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.

To post to this group, send email to masstrans...@googlegroups.com.

Kenneth Avner

unread,
May 31, 2016, 12:39:21 AM5/31/16
to masstransit-discuss
I think I will end up solving this through the state machine, possibly using an array of step-definitions (configured through the container). There are no alternative execution paths; only one path, and all I need is to be able to stop the execution at some points waiting for manual QA processes etc. Either all runs, or the process fails at some point.

I believe this must be the easiest way forward with my current limited experience with the framework. Someone please correct me if there are more elegant paths.

Dennis

unread,
Mar 23, 2017, 8:09:58 AM3/23/17
to masstransit-discuss
Hi,

Did you managed to implement your use case with routingslip at all?  I'm also new to masstransit and would like to learn what others did to implement this type of scenario.  I was thinking of implementing this scenario using saga on a controller pattern but I think it is way too chatty compared with just using the routingslip.

Thanks,

Dennis 
Reply all
Reply to author
Forward
0 new messages