Open source at Enable: cloud hosting abstractions — Enable

Open source at Enable: cloud hosting abstractions

Filed as Enable TechPosted August 24, 2018

At Enable, we benefit from, and contribute to, a number of open source projects. We believe it’s important to give back to the open source community where we can, which we do by contributing to the projects that we use daily. By raising bug reports, fixes and new features, we help make those projects even better. We also publish lots of our own code as open source libraries on GitHub.

We’ve found that participating in open source is a great way to learn. At Enable, our deve­lopers are highly-educated, highly-skilled and are driven to continue to grow their skills. By contributing to open source, our developers can explore how different projects are structured, how they operate and what tools and patterns they are using. All of the technical and leadership skills learnt by contributing to open source can be fed back into our own projects, helping us create even more delightful applications for our clients.

In this article, we’ll take a look at a couple of open source libraries we’ve developed at Enable and how they’ve made our switch to hosting applications using Microsoft Azure easier.

Open source cloud hosting abstractions

One of the most significant shifts we’ve seen in software development in recent years is the move from on-premise to cloud hosting providers, such as Microsoft Azure, Amazon Web Services (AWS) or Google Cloud Platform. At Enable, we’ve chosen Microsoft Azure as our preferred hosting platform (but, for now, we still host some systems on-premise for testing and demonstration).

Two of our own open source projects, En­able.Ex­tensions.File­System and En­able.Ex­ten­sions.Queu­ing, have been designed to help us deal with the challenges we’ve faced migrating to cloud hosting.

The first of these of these challenges we faced was avoiding locking ourselves into a single hosting platform. Ideally, we should be able to change where our applications are hosted, without needing to change any lines of code.

Another challenge we faced was dealing with differences that exist between developing software locally and hosting applications in production. We’ll look at some of these diff­er­ences below, and how we solved these challenges by developing cloud hosting ab­st­ractions.

File system abstractions

The first of the abstractions we developed at Enable was En­able­.Ex­ten­sions.File­System.

When deploying applications to Azure, we found that it was not (currently) possible to mount file shares on Azure App Services (App Services are essentially a locked down, fully managed virtual machine running IIS). Mounted file shares was how we traditionally approached sharing files between the different tiers in our applications. We therefore had to adopt a different approach.

When hosting in Azure, we now use Azure Storage, a globally distributed cloud storage service, to store our files. This meant re-writing all code that interacted with files on disk to instead store files in Azure Storage. However, we couldn’t simply replace this code, since some of our applications are hosted both in Azure and on-premise! We also don’t want to have to spin up Azure Storage accounts when developing locally.

To get around these challenges, we introduced En­able.Ex­ten­sions.File­System, an abstraction of a file system. Out of the box, this supports two file systems, a traditional files-on-disk file system and Azure Storage. With a single, common API, all of our file access code can remain completely agnostic to where files are actually stored, and with a change to a single line of code, we can switch where these files are stored.

The API provided by En­able.Ex­ten­sions.File­System contains all of the methods that you’d expect for reading and writing files. To get started with this API, install one of the available implementations, then whenever you need to work with files, simply take a dependency on the IFile­System interface. In the example below, we’re using IFile­System to get a representation of a directory and the files it contains.

using Enable.Extensions.FileSystem;

namespace Enable.Extensions.FileSystem.Samples
{
    public class HomeController : Controller
    {
        private readonly IFileSystem _fileSystem;

        public HomeController(IFileSystem fileSystem)
        {
            _fileSystem = fileProvider;
        }

        public async Task<IActionResult> Index()
        {
            var directory = await _fileSystem.GetDirectoryContentsAsync(
                "path/to/directory");

            // Do something with `directory`. Here, we’re
            // just looping over all files in the directory.
            // You’ll probably want to do something more
            // exciting in your own code.
            foreach (var file in directory)
            {
                Console.WriteLine(file.Name);
            }

            return View();
        }
    }
}

How you register IFileSystem for dependency injection (DI) will depend on the DI con­tainer that you are using. If using the DI container available out of the box with ASP.NET Core, this might look something like:

if (env.IsDevelopment())
{
    services.AddScoped<IFileSystem>(s => new FileSystem(
        @"C:\some\absolute\path"));
}
else
{
    services.AddScoped<IFileSystem>(s => new AzureBlobStorage(
        "account-name", 
        "account-key", 
        "container-name"));
}

Here, with just a couple of lines of code, we’re switching between two completely different file systems: at development time we use a local, physical file system, otherwise we store files in an Azure Storage account. This demonstrates the power of the abstraction. By changing just this one part of the code, we can change how files are stored without requiring any changes to other parts of our application that deal with files.

Even better, by taking a dependency on an IFileSystem interface, rather than a concrete file system implementation, we have unlocked the additional benefit of making all of our code that works with files unit-testable! Simply swap out your implementation of IFileSystem for a mock in your test code.

We’ve released Enable.Extensions.FileSystem with support for two files system implementations initially, but adding support for others is straight­forward. Contributions that add new types of file systems are more than welcome!

Queuing absractions

An established pattern at Enable is the use of messaging queues for asynchronous comm­unication between loosely coupled services. Queues allow applications to easily scale out workloads to multiple processors, improve the resiliency of applications to sudden peaks in traffic and naturally lead to loosely coupled applications.

When moving to the cloud, we faced the same key challenge with messaging queues that we faced when working with files: we want to be able to easily swap out our messaging system. When hosting an application in Azure we want to use Azure Service Bus or Azure Storage Queues, when hosting on-premise we want to use a self-hosted solution like RabbitMQ and in our test code, we don’t want to have to spin up any message system whatsoever!

Again, our solution to this challenge was to abstract away the differences between message queues, and so Enable.Extensions.Queuing was born!

Enable.Extensions.Queuing provides an abstraction for publishing and subscribing to messages, based around an IQueueClient interface. Out of the box, four messaging queues implementations are available: RabbitMQ, Azure Service Bus, Azure Storage Queues and an in-memory queue. As with En­able.Ex­ten­sions.File­System, this abstraction lets us easily switch out different types of messaging queues and naturally makes our trivial to unit test. Check out the documentation on our GitHub project for samples on how to make use of En­able.Ext­en­sions.Queu­ing in your own code.

We’ve released Enable.Extensions.Queuing with support for four queue im­plemen­ta­tions initially. Contributions that add support for additional queues are more than welcome!

Summary

Contributing to open source is an important part of the development culture at Enable. In this post we’ve described two of our own open source projects that have helped us successfully migrate our applications to the cloud, En­able.Ext­ensions.File­System and En­able.Ext­ensions.Queuing.

In future posts, we’ll dive into some of our other open source projects in detail and take a look at how we publish and distribute these projects.

If you’ve got any questions on these projects, or if you want to contribute ideas or features, no matter how small you think they are, we’d love to hear from you. Do get in touch.

Latest

Enable continues expansion with new offices

Following another successful year, Enable is proud and excited to announce details of our new office — Unit 15. The newly refurbished office is fully air-conditioned, and all employees are provided with high specification Apple Macs with dual monitors and Herman Miller Aeron chairs.