Implementing Long Running Business Processes (a.k.a. Sagas) Using Azure Durable Functions — Buyer’s Remorse

Manvel Ghazaryan
3 min readDec 22, 2021

In this post I want to look into modeling & implementing long running business process using Azure Durable Functions. This post assumes familiarity with Azure Durable Function concepts, so if you’re not familiar with them I’d suggest first to read relevant Microsoft documentation.

I want to look into implementing buyer’s remorse in order processing (e-commerce). Assume business requirement states:

When an order is placed by a customer, wait for X hours before proceeding to process the order. This is done, because order data analytic shows that majority of customers who placed an order and later canceled it, did it within X hours after placing an order.

Let us think of a process for a moment. Customer submits an order. System doesn’t start the processing of an order straight away. Rather it waits for X hours to see if order is getting cancelled. If not, then we can kick off order processing.

Implementation

Let’s first create an endpoint for submitting orders. This will be http triggered Azure Function which will kick off durable orchestration process which is gonna implement the business requirement stated above.

For the sake of easy testing endpoint is a /GET method, which carries order id in a query string parameter. We take the order id, check if there is an existing orchestration instance and if not, kick of an orchestration with an instance id being same as order id. In a real implementation SubmitOrder function would most probably have message binding (Storage Queue, Service Bus etc…) and we want to have some guard if the same message is delivered to us more than once.

Let’s now look at the orchestration code.

In the orchestrator we’re basically waiting for an external event CancelOrder. We specify a timeout period of 30 seconds (represents X hours from the business requirement, we need a shorter for easiness of testing), basically saying “if a CancelOrder event is not received for 30 seconds, stop waiting”. When we hit the timeout, TimeoutException is thrown, that’s why we have to wrap this call inside try/catch.

So if CancelOrder event is received, we cancel the order otherwise we hit the timeout & proceed to process the order (catch block).

Lastly let’s look into how CancelOrder event can be raised. Let’s define another http triggered Azure Function

As previously, for the sake of easy testing it is a /GET endpoint which carries order id in the query string. Upon execution it uses IDurableOrchestrationClient to raise a CancelOrder event to the orchestrator. Note how the usage of an orderId as an orchestration id ensures proper orchestration instance receives the event.

Full solution can be found on GitHub, under BuyersRemorse.

Other Implementations

My preferred tool to implement sagas is NServiceBus’ Sagas. If you’re doing SOA/microservices I encourage to take a look at NServiceBus. In my opinion NServiceBus Sagas are better suited for messaging systems. While we could also implement durable functions based on messaging, we’d either have to implement our own version of a bus or rely on a raw messaging (Azure Service Bus).

In fact buyer’s remorse implementation was influenced by an example of a NServiceBus Saga, I suggest to take a look and compare yourself.

In the next post I’ll look into implementing shipping policy using Azure Durable Functions.

--

--