What’s the right size for a microservice?

There are many opinions as to what the right size for a microservice is. Chopping an architecture into microservices of the right size is quite important. When the size is too big, a microservice becomes too complex to be built and maintained. Since the single responsibility principle is most likely violated, it invites developers to throw even more stuff into the microservice, making it even bigger and hairier. On the other hand, when the size is too small, that increases overhead in managing and deploying microservices. Also, it may result in performance issues, since the number of interprocess calls between small microservices may increase.

Some teams may use a magic rule to define a microservice’s size. For instance, there is a popular measure called the “two pizza team”. It implies that a microservice should be small enough to be developed in approximately one week by a team that can be fed by two pizzas. This originated from Amazon, where Jeff Bezos instituted a rule that team sizes must not be larger than the number of people who can be fed by two pizzas. Since Amazon was one of the earliest adopters of microservices, that’s where this belief came from.

Another approach is to restrict a microservice’s size architecturally or technologically. AWS Lambda functions, for example, are restricted to a single action. That, essentially, makes a Lambda function a nano-service. While this approach is ok for simple automation cases, building a large microservices system using Lambda functions can be very difficult. This is due to the fact that many hundreds (or even thousands) of functions are too great a number to be effectively managed by a small team.

The third approach is evolutionary. Microservice developers can start wherever it “feels right”, and over time split or merge microservices to improve the architecture. Although I agree that microservice architectures shouldn’t be carved in stone and that there should always be the option of changing microservice decomposition, it’d be nice to have more clarity as to where a good starting point would be.

Here, I’d like to share with you a few guiding principles that I myself use when decomposing a system into microservices. They are quite simple and are based on the single responsibility principle, in accordance with the “3 types” of microservices. The single responsibility principle states that a microservice shall only do one thing. And the “3 types” define what exactly that “thing” is:

Data microservice - a microservice that encapsulates a business entity. It is responsible for persisting the entity and implementing business logic that is tied to that entity. Persistence can be done using single or multiple related tables/collections - whichever is more convenient.

Business process microservice - a microservice that implements one or a few closely related business transactions. A business process microservice usually makes calls to data microservices to read or update business entities, or triggers business transactions in other business process microservices. It may also implement compensation logic to rollback transactions in case of errors.

Edge microservice - a microservice that communicates with the outside world. In fact, there are two types of edge microservices. A facade acts as an entry point into a microservices system, while a connector, on the other hand, encapsulates an outgoing connection to an external service or a system.

After you wrap your head around these microservice types, decomposing a system into microservices becomes quite simple:

1. Identify all business entities and add data microservices for each of them;

2. Identify all incoming or outgoing connections to/from your system and add edge microservices for each of them;

3. Identify complex business transactions in the system and add business process microservices for each of them.

Once you complete these 3 steps, you shall have a pretty good decomposition, which may look somewhat like this:

However, that’s not the end. As you know, microservices have two pretty big problems: performance and data integrity. And microservice boundaries have a critical impact on those issues. So, to make the right architecture, one more step is needed:

Identify critical business transactions that require high performance and/or solid data integrity and trace those transactions through the microservices in your architecture. If you see problems, consider changing microservice boundaries and merging a few microservices together, as an option.

The last step can make your nice and clean architecture a bit messier, but it’s better to have a “messy but working” architecture, than a “clean but broken” one. So give it a try, and see what works best for you.