How small should Microservices be - and where to split them?

This is one of those questions that keep popping up - how small should microservices be? I often find myself saying "it depends..". There is no good rule of thumb for this, but I will give some guidelines and questions you can ask yourself when in this situation to make a decision.

Modelling around a business domain

The goal is not to make your service as small as possible. Which is why "Micro service" is a bit misleading. It does not have much to do with size as you cannot "measure" if your service is too big (or small). You need to think of the size of your microservice as you do about the size of your codebase. A large codebase is not a sign that something is wrong. As Bill Gates is often quoted has having said: "Measuring software productivity by lines of code is like measuring progress on an airplane by how much it weighs.". Some weight is indeed necessary, but there is no reason to overdo it. Number of endpoints, dll's or lines of code is not a good measure for your microservice. You should not create a new service just because you have hit a 1000 lines of code for example.

To get started we look at the single responsibility principle. The single responsibility principle states that:

every module or class should have responsibility over a single part of the functionality provided by the software

It could be said that microservices should have a single responsibility. It may have the responsibility to keep track of customer data and do CRUD operations on this type of domain object. Sam Newman states that microservices should be "modelled around a business domain" in his Principles of microservices. Which is exactly the case when your microservice simply handles customer data. Applying the single responsibility pattern will get you far in this case. But this is also a highly simplification on everyday scenarios. But the overall idea here is to avoid having services that can do "everything".

High cohesion

So let us say that we have adhered to the single responsibility principle and split our services accordingly. They own their own business domain. Let us say that we have a customer and an address service. However in all the scenarios where we need customer data we also need address data. This is a bad design - as we now have twice the calls. We need to contact both our address and customer service every time. It may also be that changes to our customer service always will result in changes of our address service or vice versa. This is a design where things are too decoupled. Again we can use another principle from programming - high cohesion - "High cohesion means to keep similar and related things together, to couple or fuse parts which share content, functionality, reason or goal". Simply put: should not split that which belongs together.

However was the scenario that you rarely needed the address of a customer together with a customer. Then they probably should be split. Again I like to think in terms of whether a change to the customer service would create a change for the Address service.

Continuing on high cohesion. I previously developed a set of services, where I had split the validation from a service into a separate service. So for example I had my Customer service which took care of the CRUD. But instead of sending data to this, a proxy service was called which validated the data. Which then would call the customer service if it was successful. This made no sense - it should be part of the Customer service domain to validate it's own data. The service was also only capable of validating customer objects, so it would never be used without the customer service. It made even less sense when every call had to check whether this customer had already been created. So the validation service needed to call the customer service to see if it was already there. The split made everything much more complex. Where the initial idea was to make the Customer service simpler, it made the whole flow more complex.

As some people like to say: Keep it simple stupid. Had the above been the case of a loadbalancing service it would have made more sense. The biggest mistake that I see is that developers often take layers of a domain and split that into multiple services. Such as the validation example above.

Summary

So here is my small checklist of questions for when to split services:

  • What are the domain/responsibility of the services?
  • What domain objects / interfaces will the services have?
  • Will both the services always be used together?
  • Will a change in one service always cause a change in the other?

Other reasons?

There may be other reasons to split your services than the ones tied to modeling it around a business domain. Take a look at this post by "Grygoriy Gonchar" for some other technical, team or security related reasons to split a service.

That is it

One of the hardest things when working with microservices is to split/cut them just right. It is almost the same exercise as writing code and trying to figure out what goes in which class. This often creates some discussions in development teams which is great.

Do you have a good case in your mind where you wonder if you should have split your service? or do you highly disagree with my post? Feel free to let me know in the comments below!

As a closing note: That it takes too long to create a new service is not a good reason not to split them. It should be fast to create a new service so developers do not hesitate to do this. "reusing" an old service for a new purpose is definitely a smell.