GO TO:
What is Azure Durable Function?
Usually, the system we develop isn’t just “a set of functionalities or modules” (Function-as-a-Service), but rather “a set of multiple functionalities and modules influencing one another, dependent and interacting”. Thanks to the Azure Durable Functions, we are able to master this process. Azure Durable Function is the orchestrator controlling the entire business process, meaning our workflow. Durable Function is an extension of Azure Functions which allows you to optimize processes in a serverless environment.
Azure Durable Function consists of 3 basic types of blocks:

Client Function
A kind of starter that gets the process up and running. It is most often triggered by an http request, adding a record to the database, uploading a file, etc.
Orchestrator Function
The process that controls our workflow – this is where we set up the sequence of steps in the process.
Activity Function
A specific functionality, e.g. retrieving a record from a database, sending an e-mail, issuing an invoice, etc.
![]() EXPERT KNOWLEDGE Using AWS Lambda functionsDevelopment of serverless applications Read the article! |
Let’s explore Azure Durable Functions in practice
For the purposes of the article, let’s assume that we run a web fashion store, and after the payment has been credited, we want to present the customer with possible color versions and, once the decision has been made, close the order. The initial process would look like this:

Now let’s try to answer the following questions:
- How can we monitor what stage of the process the client is at?
- How can we spot any errors?
- Can we retry the step in the event of an error?
- Can we parallelize the steps?
- How can we receive confirmation of the event, i.e. the customer’s choice?
As you can see, there are a number of problems. The process is not perfect and cannot be easily controlled. Let’s get started and make sure it’s done right!
We begin with the ‘starter’:
[FunctionName("OrderStarter")] public static async Task<IActionResult> HttpStart( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { log.LogInformation("Received a payment"); string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); var order = JsonConvert.DeserializeObject<Order>(requestBody); // Function input comes from the request content. string instanceId = await starter.StartNewAsync("OrderOrchestrator", order); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); return starter.CreateCheckStatusResponse(req, instanceId); }
We have created a function that is triggered by an http request and receives information about the order. Its key element is the type object IDurableOrchestrationClient, which starts the selected process.
![]() EXPERT KNOWLEDGE Microservices architecture explainedIs it still a revolution or already standard in IT projects? Go to article! |
Azure Durable Functions – now let’s use the orchestrator
In its initial, simplified form, it might just look like this:
[FunctionName("OrderOrchestrator")] public static async Task OrderOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context,ILogger log) { var order = context.GetInput<Order>(); { await context.CallActivityAsync<string>("PostThePayment", order); await context.CallActivityAsync<string>("GenerateColors", order); await context.CallActivityAsync<string>("SendNotification", order); await context.CallActivityAsync<string>("CloseOrderProcess", order); log.LogInformation($"Order processed successfully."); } }
As you can see, it’s a place from which we can control the entire ordering process.
To put it very simply, without diving into too much detail, the individual steps of the process, i.e. the Activity Function, can look like this:
[FunctionName("PostThePayment")] public static Task PostThePayment([ActivityTrigger] Order order, ILogger log) { log.LogInformation($"Do some stuff with order."); }
Let’s make modifications to find clear answers to the questions previously asked:
[FunctionName("OrderOrchestrator")] public static async Task OrderOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var color = new List<string>() { "white", "black", "pink", "green" }; var order = context.GetInput<Order>(); try { await context.CallActivityWithRetryAsync<string>( "PostThePayment", new RetryOptions(TimeSpan.FromSeconds(5),3), order); var tasks = new list<Task<string>>(); foreach (var color in colors) { var task = context.CallActivityAsync<string>("GenerateColors", order); } var generatedColors = await Task.WhenAll(tasks); await context.CallActivityAsync<string>("SendNotification", order); var userDecisionResult = await context.WaitForExternalEvent<string>("Approval", TimeSpan.FromHours(24)); if (userDecisionResult == null) { //time out - user not approved his choice in 24 hours //cancel order } if (userDecisionResult == "approved") { //do some activity when user approves his choice } await context.CallActivityAsync<string>("CloseOrderProcess", order); log.LogiInformation($"Order processed successfully."); } catch (Exception e) { //log exception, do some necessary cleanup } }
Now we can see the significant changes:
- By using the method CallActivityWithRetryAsync we are able to repeat the step in case of an error.
- Thanks to using the Fan-out Fan-in pattern, we made it possible to generate color versions much faster by parallelizing the processes.

- As you can see, through orchestration, we can easily use the construction Task. WhenAll to achieve the above goal.
- By using the pattern Human Interaction we have managed to simulate the system waiting for user confirmation within a given period of 24 hours.

The last question is: how do we monitor the state of the process? By default, after starting the orchestrator, we get a set of links to the list of workflow operations:
[FunctionName("ConfirmUserChoice")] public static async Task ConfirmUserChoice( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req, [DurableClient] IDurableOrchestrationClient client, ILogger log) { string result = req.GetQueryParameterDictionary()["result"]; string orderId = ""; //get orderId from db or other storage await client.RaiseEventAsync(orderId, "Approval", result);
So to monitor the current status of the process, just call statusQueryGetUri and as a result, you will get, for example:
{ • id: "35e9c2d4b7914d34a8c79e171587406f", • statusQueryGetUri: "http://localhost:7071/runtime/webhooks/durabletask/instances/35e9c2d4b7914d34a8c79e171587406f?taskHub=TestHubName&connection=Storage&code=i0BkAM8H9xo64Lve9oYAJvBstz7vzobVSHCKsKziaYXdXe4YCEhz0g==", • sendEventPostUri: "http://localhost:7071/runtime/webhooks/durabletask/instances/35e9c2d4b7914d34a8c79e171587406f/raiseEvent/{eventName}?taskHub=TestHubName&connection=Storage&code=i0BkAM8H9xo64Lve9oYAJvBstz7vzobVSHCKsKziaYXdXe4YCEhz0g==", • terminatePostUri: "http://localhost:7071/runtime/webhooks/durabletask/instances/35e9c2d4b7914d34a8c79e171587406f/terminate?reason={text}&taskHub=TestHubName&connection=Storage&code=i0BkAM8H9xo64Lve9oYAJvBstz7vzobVSHCKsKziaYXdXe4YCEhz0g==", • purgeHistoryDeleteUri: "http://localhost:7071/runtime/webhooks/durabletask/instances/35e9c2d4b7914d34a8c79e171587406f?taskHub=TestHubName&connection=Storage&code=i0BkAM8H9xo64Lve9oYAJvBstz7vzobVSHCKsKziaYXdXe4YCEhz0g==", • }
We see that the process is now over and it can be considered completed.
Benefits of Durable Functions
- Durable Functions are based on Microsoft Azure – a platform that is used by hundreds of thousands of people on a daily basis.
- Azure Durable Function can work in serverless mode, popularly known as pay-as-you-go – that is, you pay only for the time you use the service.
- You can scale it freely depending on the load. There is no need to implement or set anything. The function will scale to such an extent as to speed up the task execution as much as possible.
- Each functionality can be divided into separate, independently developed modules.
- You can write each functionality (function) using a given language, e.g. C #, Java, JavaScript, Python etc.
Azure serverless functions – summary
To sum up, thanks to the use of Azure Durable Function, you can freely create complicated workflows and fully control them. Native functions can work with the entire cloud ecosystem, so the possibilities of expansion are endless. What’s more, there is an option to use containers of functions in the on-premise environment – but that’s a matter for a separate article.