Open source at Enable: cloud hosting abstractions

Image

Introduction

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).

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

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.

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:

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.

New features every 6 weeks: what’s included in our scheduled software updates

Your business never stands still — so neither does our software. Remaining up to date with the latest technology keeps your business ahead of the pack by providing you with new or enhanced features and reducing potential security risks. In addition to these, they can also improve software performan...