Director E2E Video Architecture & Service Design
First they gave us Servers, so we built Service-Oriented Architectures.
Then they gave us Containers, so we built Microservices.
Now they give us Event Handlers, so we’ll build Cloud Functions.
As we made our hosting platforms more amenable to deploying smaller units, so have our applications broken down into smaller software packages. There are many reasons for this, and there are diverging opinions on whether it’s a good thing. But if we look back at the original concepts behind cloud computing, there was a dream of having code distributed infinitely in a network of connected computation nodes. And we’re getting a little closer with the emergence of serverless platforms.
We cannot go too small because we have blocks (procedures) that consist of sequences of computational steps that require shared memory in order to execute effieciently. Then we wrap those around a function definition, that defines a contract for its input and output, which allows it to be composed with other such functions.
This approach has been very successful in the architecture of Unix, and is one of the reasons for its longevity and ubiquity. I don’t mean to suggest that Web applications should follow a comparable cloud-based shared eco-system (although some are trying), but I do believe that we could benefit from applying similar principles when building Web applications.
Beyond function definitions, we also group closely related functions in modules. An example would be the CRUD operations for data within a given domain (e.g. user management). Those tend to share code, such as common object models, parsing logic, formatting, etc. So if were to deploy individual functions in serverless environments, we’d end up with a bunch of duplicated code.
But let’s assume we don’t mind duplicated code deployments. After all, we can still manage it properly in our code repositories. We might still want to share temporary resources, though, such as database connections. We might also want to make sure that all operations for the same domain are deployed and managed as a single unit.
It fits well with the Single Responsibility Principle:
Gather together those things that change for the same reason, and separate those things that change for different reasons.
So, Node.js is great for Microservices. And it’s also great for writing smaller function modules. And Express is great for building Web application in Node.js.
However, most of these serverless environments (AWS Lambda, Google Cloud Functions, etc) already provide a lot of facilities out of the box to handle the common functions of a Web server. And for these Nanoservices, which should not have more than a handful of functions, we really shouldn’t be bothered with the overhead of complex Web server logic. We should certainly leverage HTTP, as it is the ubiquitous transport mechanism for transfering messages between Web services, but we should do it in a more RPC kind of way.
This where most current frameworks offer too big of a tool for the small footprint that we’re looking for. If anything, I’d argue that going serverless should allow us to free ourselves from frameworks, and focus instead on building purer functions. There is, however, still a need for basic routing within a Nanoservice in order to map incoming request to the appropriate handler function. Also, because of the proprietary nature of these commercial serverless environments, there is a case to be made for a certain level of abstraction, so that our functions can be decoupled from the specifics of the platform they’re executed in.
I think that more applications will surface soon that are focused on enabling functional programming applied to serverless deployments, which I’m very hopeful about because it feels like a step in the right direction. Certainly there are many real-world considerations still to be addressed, such as latency, performance, memory usage, etc. But just like with Microservices, I believe that we’ll find the right set of tools and practices to make this, not just practical, but also highly performant on real-world applications.
I wrote a small package to address these needs when I started deploying serverless applications: modofun.
It carries no additional dependencies, because we want our deployments to be as small as possible, and it’s limited to about 100 lines of code. It aims to add just enough (and not any more) functionality to make it easier to deploy function modules on serverless platforms, with the added benefit of leveraging existing middleware built for existing frameworks like Express. Here are a few of its features:
It makes it easy to expose an existing module as Serverless cloud functions:
An intentionally simplistic router maps incoming requests to functions, and passes the additional components of the URL path as function arguments. Additional request data is also available as context (this) for the function invocation.
When applying modofun to a function module, you can also re-use existing middleware components to be executed everytime, or only for certain functions (more details in the documentation). It then returns a handler for events generated by the serverless platform (currently supports AWS and Google Cloud).
Get it with npm:
npm install modofun
There’s also a real-world example that includes exposing a GraphQL endpoint, a Google Cloud Functions deployment using the alternative Express-like request/response mode, and an AWS Lambda deployment. All using modofun, combined with Babel and other cool technology.
Create your free account to unlock your custom reading experience.