Azure Functions Code Reuse

Posted by

Azure Functions gives us the ability to quickly write functions that can scale to incredible levels without skipping a beat. But once you’ve spun up a few functions, some re-usability patterns begin to emerge: an entity that could be used across Function Apps here, duplicate code that could be extracted there.

There are a few ways that this common code can be shared in Azure Functions. Before looking at those specifics, I want to spend a bit of time figuring out which parts of a function could be reusable.

Potentially Reusable Code

The code reuse patterns below depend on the design and complexity of your serverless application. A single Function App that has only one or two functions with little to no business logic isn’t likely to have much reusable code.

On the other hand, serverless apps that are composed of multiple Function Apps, each with many functions, are more likely to have some common code. So which parts of a function are candidates for reuse?

  • A trigger function should ideally hand off to a service class that does the grunt work of the function. With that in mind, it’s unlikely that much code is reusable.
  • The business and data access logic is definitely in the running. It’s common to see a service or repository class used in more than one place.
  • Entities and DTOs are often needed across Function Apps so that the functions can speak to each other in the same ubiquitous language.
  • Frameworking and utility code is likely to be needed across Function Apps.

That’s a fair amount of code that can be used across functions, even in smaller applications. So let’s clarify how to go about reusing some of the common code areas we’ve identified.

Within A Function App

This one’s fairly obvious but I’ll mention it anyway. For code that is to be used by another function within the same Function App, it’s as easy as extracting the common code into a separate class, and using it in both functions. You can see an example of this in my sample project on GitHub, which I’ll explain next.

I’ve written two functions, Scheduler and Validator, that need to respectively write and read flight information stored in a table. To accomplish this, I created a Store folder at the root of the Function App project, and created a repository class named FlightStore that persists and retrieves the data:

withinfunctionappsharingsln

I then use the FlightStore repository to access the flight information from the two function triggers. Below is the code from the Validator function:


        static FlightStore _store = new FlightStore();

        [FunctionName("Validator")]

        public static async Task Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log)

        {

            var flights = await _store.Get();

            if (flights.IsEmpty)

            {

                log.LogInformation($"No flights scheduled.");

            }

            foreach (var flight in flights)

            {

                // Perform some random validation for now

                log.LogInformation($"Flight {flight.Id} is valid.");

            }

        }

The function trigger calls the Get() method to retrieve all the flights, and then perform its validations. Any other function that needs the same data can do so in the exact same way.

This approach works well for any code that needs to be shared within a Function App, like business or data access logic. Now let’s look at how to do the same thing when multiple function apps are involved.

Across Function Apps

Here’s where things get interesting, with two different approaches to look at. The first, using NuGet packages, is likely to be familiar to many developers. The second, which I prefer, tends to accelerate the development workflow, and is easier to implement and maintain, by way of a class library.

Choosing a direction depends partly on the structure chosen for the functions repository. The NuGet approach is likely you’re only choice if each Function App is in its own solution or repository, whereas a class library-based approach will work better if you’ve got all your Function Apps in a single Visual Studio solution. I’ll explain why that is in just a bit.

NuGet Packages

Every .NET developer is at least somewhat familiar with NuGet packages. But while it’s simple to add packages to a project, using NuGet comes at a cost: each NuGet package that you create and consume adds extra turnaround time to the development process since every change needs to be written, built, packaged and pushed to a feed before use. The process is highly rigid and can lead to lots of thumb twiddling as a package is prepared.

The use cases that make the most sense to leverage NuGet packages for code reuse are:

  • When code is unlikely to change frequently, like a generic class to handle trigger events, or helper classes that make it easier to write functions.
  • When the code has few dependencies on anything else. Otherwise, it’s all too easy to end up in a situation where you’re locked in to a specific version of a package due to mismatched dependency versions across packages.

Class Libraries In The Same Solution

The second approach to code reuse is to create class libraries where common code can live. I tend to prefer this approach at the start of a project because it’s much quicker to implement than NuGet packages. It’s also easy to move the code to a package once it reaches a certain level of maturity.

There is a caveat of course: the class libraries must be in the same Visual Studio solution as the Function Apps that you plan to share the code with. Here’s what that looks like in my sample project:

acrossfunctionappsharingsln

The Common class library contains an implementation of an ObjectResult for 500-level errors that will be used in the HTTP triggers in both Function App projects, Flights and Planes. The class library targets .NET Standard 2.0, and is referenced in both Function App projects.

The beauty of this approach is that any change to the code in the library will immediately affect all functions that use that code. Combined with a thorough testing strategy, undesired side-effects will be detected early on in the development process with this approach.

The class library based approach also makes it easier to see the impact of a change on the application as a whole. Here’s what happens when I change the signature of a dependency in one of the functions:

contractchange

The code simply doesn’t compile, making me immediately aware of which consumers will be impacted by the change. However, if I had packaged this shared code in a NuGet package, I might not be aware of the impact until I update every last consumer of the package. Since an update might not be needed until some time in the future, the problem would remain hidden until then.

Class libraries make the most sense to share code when:

  • The project is in its infancy and might change significantly before becoming stable.
  • A single Visual Studio solution is being used for all Function Apps.
  • You want to easily see which functions are impacted by changes.

Try It Out for Yourself

As we saw, there is no silver bullet to reusing code in Azure Functions. I encourage you to experiment with both approaches to see which you find best, and let me know your thoughts in the comments below.

3 comments

  1. In our team, we are discussing the exact same thing. The issue with the solution with all functions and shared code is that they would then also by default share the same code repository, which makes it really difficult to work with. Because one of the points with functions is that they can be developed and deployed independently of each other. Any thoughts on that?

    Like

    1. Hi Niels. You’ve summarized the code re-use dilemma quite nicely. The conclusion I’ve since come to is that class libraries in a single repo are a great way to plan for a Function app to eventually be split into multiple Function apps. As you grow to many Function apps though, it’s time to re-think your tooling and move those frequently used libraries to NuGet packages for a truly scalable code solution. I’ve yet to come across any other viable solutions.

      Hope that helps, and let me know how it turns out for you!
      Marc

      Like

  2. Having been with a company that went extensively with in-house NuGet – I can tell you this is less than ideal or effective solution. Constant changes to packaged code requires constant updates of all clients, and as noted above breaking chngs don’t surface all the time, especially when unit testing not vigourously done.
    Shared code is far better imo – but you failed to discuss how to version this. Using Git submodules? subtrees? Or neither? I prefer the 3rd option and enforce a folder pattern for devs to clone repos (so that path-relative folders can work). This is easiest to do with shell scripts, coupled with comprehensive README.md that identify dependencies. Also to prevent Pull Req’s from pulling in shared code chgs.

    Like

Leave a comment