Logic Apps or Durable Functions: Which workflow solution approach suits me best?

Microsoft Azure offers two services on its platform that enables developers to create a workflow: Azure Durable Functions and Logic Apps. Azure Durable Functions, a cloud implementation of the durable task framework, offers you a way to define a workflow using code. While with Logic Apps you can automate a workflow without writing any code.

In this blog post, we will discuss which service you should use when running workflows in Azure. Furthermore, we will present a use-case scenario using Durable Functions and one using Logic Apps.

Logic Apps

You can run a business workflow in Azure using the Logic App service. Moreover, the Logic App is a logical container for one workflow you can define using triggers and actions. The trigger instantiates a workflow, which can consist of one or many activities (actions). For instance, you can trigger a workflow by sending an HTTP request or schedule a workflow every hour to retrieve data from a public website. The latter is a natural, straightforward and excellent use-case for a Logic App when you need to process the data and persist it in a database in Azure such as Cosmos DB.

Logic Apps or Durable Functions

Assume you want to get the weather forecast at every hour of the day for the next 24 hours and store it in a document collection in Cosmos DB. You can call an API like OpenWeatherAPI using a recurrence trigger in a Logic App. Next, you insert the response from the call in Cosmos DB document collection. A simple scenario with ingesting public data and persist in a document store.

With our Logic App, we use a custom connector to communicate with the OpenWeatherMap API. For more details on how to create the customer connector for this API see the Middleware Friday episode Building custom connectors for Logic Apps. The call to the API through the custom connector occurs every hour based upon the recurrence trigger.

Logic Apps or Durable Functions

The output of the call is parsed using the Parse JSON action, and the body token is used for insertion to document store in Cosmos DB.

The documents (output) of each recurrence run can be found in the document store in Cosmos DB.

Logic Apps or Durable Functions

This scenario is built and operational within an hour and is useful for a simple workflow ingesting and storing data using a recurrence (schedule).

Durable Functions

With Azure Durable Functions, you can write stateful workflows in a new function type called an orchestrator function. This orchestration function provides you more control for building workflows than using, for instance, a designer for Microsoft Flow or Logic Apps.

A developer writes code with Durable Functions; no designers, nor a JSON DSL that you need to learn. It is strictly coded, and developers can build durable functions with the code the same way as they are familiar with Visual Studio or Code.

The critical difference with Logic Apps is control – with Durable Functions you can write code and have fewer restrictions. With Logic Apps you are being bound to triggers and actions it provides – the action offering a wide variety in flow control, connectivity, behavior, and message handling, but still less flexibility than with code.

Suppose you want a flexible recurring process in a workflow, for instance, polling until certain conditions are met such as a traffic jam is cleared or weather clears up. You can implement such a scenario with a monitor pattern, ending its execution once the condition you set specify is met. More details are available Monitor scenario in Durable Functions – Weather watcher sample.

By implementing the monitor pattern leveraging Durable Functions, you have more flexibility and power of a programming language such as C#. Furthermore, you can run the function cross-platform in a container.

An example of a Durable Function Solution would be to build a monitor for local weather. When the weather is clear, then you will receive an email, if not then it will keep monitoring the weather on a given schedule (number of hours). Once the weather is clear you will get an email.

Logic Apps or Durable Functions

In this scenario, we have an Orchestrator client responsible for starting and stopping orchestrator functions and monitoring them. In our case, we have an HTTP-triggered function as orchestrator client, which accepts an HTTP request.

#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"

#r "Newtonsoft.Json"

using System.Net;

public static async Task<HttpResponseMessage> Run(

    HttpRequestMessage req,

    DurableOrchestrationClient starter,

    string functionName,

    ILogger log)

{

    // Function input comes from the request content.

    dynamic eventData = await req.Content.ReadAsAsync<object>();

    // Pass the function name as part of the route

    string instanceId = await starter.StartNewAsync(functionName, eventData);

    log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

    return starter.CreateCheckStatusResponse(req, instanceId);

}

Next, the orchestration function contains the workflow – consisting of code statements calling other functions like activity for checking if the weather is clear or sending the message for clear weather.

#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"

#r "Microsoft.Extensions.Logging"

using System.Threading;

public static async Task Run(DurableOrchestrationContext monitorContext, ILogger log)

{

    MonitorRequest input = monitorContext.GetInput<MonitorRequest>();

    if (!monitorContext.IsReplaying) { log.LogInformation($"Received monitor request. Location: {input?.Location}. Email: {input?.Email}."); }

    VerifyRequest(input);

    DateTime endTime = monitorContext.CurrentUtcDateTime.AddHours(4);

    if (!monitorContext.IsReplaying) { log.LogInformation($"Instantiating monitor for {input.Location}. Expires: {endTime}."); }

    while (monitorContext.CurrentUtcDateTime < endTime)

    {

        // Check the weather

        if (!monitorContext.IsReplaying) { log.LogInformation($"Checking current weather conditions for {input.Location} at {monitorContext.CurrentUtcDateTime}."); }

        bool isClear = await monitorContext.CallActivityAsync<bool>("GetIsClear", input.Location);

        if (isClear)

        {

            // It's not raining! Or snowing. Or misting. Tell our user to take advantage of it.

            if (!monitorContext.IsReplaying) { log.LogInformation($"Detected clear weather for {input.Location}. Notifying {input.Email}."); }

            await monitorContext.CallActivityAsync("SendGoodWeatherAlert", input.Email);

            break;

        }

        else

        {

            // Wait for the next checkpoint

            var nextCheckpoint = monitorContext.CurrentUtcDateTime.AddMinutes(30);

            if (!monitorContext.IsReplaying) { log.LogInformation($"Next check for {input.Location} at {nextCheckpoint}."); }

            await monitorContext.CreateTimer(nextCheckpoint, CancellationToken.None);

        }

    }

    log.LogInformation("Monitor expiring.");

}

private static void VerifyRequest(MonitorRequest request)

{

    if (request == null)

    {

        throw new ArgumentNullException(nameof(request), "An input object is required.");

    }

    if (request.Location == null)

    {

        throw new ArgumentNullException(nameof(request.Location), "A location input is required.");

    }

    if (string.IsNullOrEmpty(request.Email))

    {

        throw new ArgumentNullException(nameof(request.Email), "An email is required.");

    }

}

public class MonitorRequest

{

    public Location { get; set; }

    public string Email { get; set; }

}

public class Location

{

    public string State { get; set; }

    public string City { get; set; }

    public override string ToString() => $"{City}, {State}";

}

The activity functions will perform one task only like sending a message using SendGrid binding:

#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"

public static void Run(string email,out SendGridMessage message)

{

    message.SetFrom(new EmailAddress(email));

}

Wrap up

With this blog, we hope you get a better understanding of creating workflows in Azure using either Durable Functions or Logic Apps. The choice between the two is on how much control you need and how complex is your target solution.  Lastly, we recommend also to read a blog post from Paco de la Cruz written in May of this year providing a great comparison between Durable Functions and Logic Apps.

Serverless360 is a one platform tool to operate, manage and monitor Azure Serverless components. It provides efficient tooling that is not and likely to be not available in Azure Portal. Try Serverless360 free for 15 days!

Serverless360-Blog-CTA

Author: Steef-Jan Wiggers

Steef-Jan Wiggers is Azure Technology Consultant at Codit, is all in on Microsoft Azure, IoT, Integration, and Data Science. He has almost 20 years’ experience in a wide variety of scenarios such as custom .NET solution development, overseeing large enterprise integrations, designing and building API's and cloud solutions, managing projects, experimenting with data, SQL Server database administration, and consulting. Steef-Jan loves challenges in the Microsoft playing field combining it with his domain knowledge in energy, utility, banking, insurance, healthcare, agriculture, (local) government, bio-sciences, retail, travel, and logistics. He is very active in the community as a blogger, book author, InfoQ editor, and global public speaker. For these efforts, Microsoft has recognised him as a Microsoft MVP for the past nine years. Steef-Jan's can be found on twitter at @SteefJan.