Serverless: Focusing on Actors and their Activities

Applying the Single Responsibility Principle

John Gilbert
4 min readJan 26, 2022

Driving down lead time, shortening the feedback loop, moving fast and iterating, and evolving our systems as we learn, is the holy grail of software development. Independently deployable microservices are supposed to help us achieve this. But the question arises: How small should a microservice be? The common answer is to follow the Single Responsibility Principle (SRP), but the SRP is commonly misunderstood.

This is understandable, because the name implies that a module should do one, and only one, thing. But this was never the definition of the SRP, and yet it has been repeated over and over again. This misunderstanding has arguably led to the creation of many microservice death stars. These death stars are resistant to change and ultimately drive up lead time, because of the complex dependencies within these systems.

The original definition of the SRP is that a module should have one, and only one, reason to change. This is better, because the operative word is change, but it is ambiguous, because it doesn’t zero in on what drives change. The short answer is that people (that is, actors) drive change. Uncle Bob (the creator of SRP) acknowledged this problem and refined the definition in his book Clean Architecture, as follows:

a module should be responsible to one, and only one, actor

Armed with the recognition that people are the drivers of change, we can start to architecture our serverless software systems so that they enable change.

Three Kinds of Actors

In my books I zero in on three kinds of actors: Users, External Systems and Business Owners. These kinds of actors drive change in different ways.

  • Users perform many different activities and each can change independently.
  • External Systems are owned by separate entities and will change at the whim of their owners.
  • Business Owners control the overarching business processes and can change these policies without necessarily impacting the individual activities.

These kinds of actors lead to the main autonomous service patterns that we can leverage to create serverless systems that facilitate change:

Autonomous Service Patterns
  • Backend for Frontend (BFF) services support end users, and the micro frontends they use to perform their activities, and they produce events to record the work performed.
  • External Service Gateway (ESG) services create an anti-corruption layer around external systems and bridge these intersystem gaps by exchanging events.
  • Control Services encapsulate the policies and rules that govern business processes and orchestrate the flow of the activities.

These patterns help us draw the lines between the types of logic our systems need to perform. Next we need to group the logic into right-sized services that can change independently.

Focus on verbs instead of nouns

It is common to find microservices that are organized around the nouns in a system, such as an OrderService that is responsible for everything related to Orders. However, this creates a system that is resistant to change, because a change to a noun-focused service can have a ripple effect across the system.

Instead, we want to focus on the verbs in a system. Event Storming is a technique that can help us identify the verbs and events in a system, the actors and the activities that generate these events, and when these activities are performed relative to each other.

An activity is the right-size for defining how big or small a service should be, so that it can change independently. For example, a BFF service should support a specific user activity. The size of the service is bounded by the nature of the activity.

Regardless of the activity the functions in a BFF play very specific roles. The listener function consumes the necessary upstream events to materialize the necessary data, the rest function provides the queries to retrieve the data and the commands to act on the data, and the trigger function produces domain events to make a permanent record that the activity was performed.

This leads to the following:

a service should support a single activity for a single actor

This activity focused approach results in a very flexible and evolutionary system that helps us drive down lead times, because we can easily and independently add, remove, rearrange and change these activity focused services. The system becomes reactive because we have created an Inversion of Responsibility whereby an upstream service relinquishes responsibility for what happens after it performs its activity. It is up to downstream services to decide how they should react. We can continue this chain reaction infinitum to build elegant serverless systems.

--

--

John Gilbert

Author, CTO, Full-Stack Cloud-Native Architect, Serverless-First Advocate