Serverless and Functions-as-a-Service

Serverless Operational Concerns

At this point, we have a system that exemplifies a functions-only, event-sourced architecture. We can see the advantages in separating plumbing (like routing web requests) from our business logic. If we assume that our cloud service provider (like AWS) handles that plumbing and event-routing for us, that means that the thing we deploy to production is a bunch of functions that adopt a protocol. Our cloud provider will be in charge of making them execute.

We've seen some benefits from a system design perspective, but this also has great operational benefits.

In a typical MVC-style web app, the unit of deploy is an entire application and we're expected to manage all parts of it, including what the framework provides. Below is the architecture of such a system, with «framework» denoting parts of the framework we didn't write, but are running, and «managed» denoting components our cloud provider handles. Anything not «managed» is our responsibility, even if we didn't write it:


Click to embiggen

I've been pedantic about framework-provided subsystems for a reason. Because we are asking our hosting provider to run our entire application, we are responsible for the operations of that application including the framework and library code. Our hosting provider doesn't care that some open source team maintains our database adapter—we are the ones choosing to run it in production.

If we compare that to our functions-as-unit-of-deploy, there's less stuff to manage, and each function has a smaller footprint.

Consider if our tiny application was implemented as an MVC-style web framework. We would need to have visibility into the behavior of every piece of the framework as described in the above diagram. To understand how our application performs, we have to examine both our code and the framework code. If you've used an Application Performance Monitoring (APM) tool like New Relic, you know how complex this can be, especially if your web framework or programming language doesn't have a lot of hooks for it to instrument.

You end up having to litter your code with stuff like this:

db.createObject(
  // "nr" is the custom tracing library
  nr.createTracer('db:createObject', function (err, result) {
    if (util.handleError(err, res)) {
      return
    }
    res.write(JSON.stringify(result.rows[0].id))
    res.write('\n')
    res.end()
  })
)

When we design our system as functions responding to events, all of which is managed by the cloud services provider, it gets simpler and we don't need very much custom tracing junk:


Click to embiggen

We're still using the same general components, but a few things are different:

There are theoretical cost benefits, too. Our function that renders HTML likely gets called a lot, but our function that sends email likely not nearly as much. The way most Faas providers work, you only pay for when your function executes, as opposed to paying for server processes or virtual machines that are always on.

So, a serverless architecture based on event-sourcing, hosted by a cloud Faas provider has many advantages that we've seen. The programing model is attractive, the operational model is simplified, and it could cost less. There can't be any downsides, can there?