Should you use microservices?
Microservices have been around for more than a decade and yet so many still don’t get it right. I am no different, I have created some pretty coupled architectures in my pursuit to build microservices. But similar to programming, having failed many times and knowing how not to do something is also a success.
I recently posted my decision tree to twitter for choosing between a Microservice architecture and a Monolith for a greenfield project. It blew up more than I thought it would, but I got some pretty good feedback, click on the above tweet or see the original image below.
The revised diagram (thank you internet)
The revised diagram is a direct result of all the feedback I got. I could not fit it into a decision tree 🤷♂️ anymore, so have a table instead:
Important points raised
Team size and communication plays a huge factor in choosing the right architecture.
Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure. - Melvin E. Conway (1967)
Bringing Conway’s law into this discussion, translates to multiple teams will gravitate towards microservices and a large singular team will tend to build monolithic software.
If you are doing microservices as a single team, then be prepared for the cognitive overhead and discipline that is needed to properly implement microservices. Many times it does not make sense from a business perspective, you will most likely have less developing velocity and increased cost.
Your engineering standards have to be really high to do microservices as a single team. On the other hand, multiple teams where each team owns at least one service can quickly lead to disjointed communication if the right processes aren’t followed.
Serverless != Microservices
Another misconception that I often see, is the belief that as soon as you are doing Serverless, you are doing Microservices. Just because you have 100’s of lambdas for your API does not mean you are doing Microservices. It is still a Monolith if it is a singular deployment with business logic shared between the lambdas.
Challenges of microservices
In the original diagram I had limited space but decided to pad the empty space with some of the most important points, but could not include all of them. Here it is again, including what I didn’t mention and some that was also raised by others:
- Schema versioning and management is difficult, you need to honor backward and forward compatibility.
- Transactions become distributed, needs to be done with the saga pattern, event choreography or event orchestration
- Data duplication and consistency make bulk changes (or “fixes”) to entities difficult. You can not just update all rows in a DB column. Events need to be emitted to other services as well
- Synchronous service communication introduce latency and are unavoidable at times. It is easy to inadvertently create long service call chains between your services, leading to over chatty and dependant services.
- Asynchronous service communication introduces eventual consistency, this usually requires systems to handle failures and be idempotent as well.
- End-to-end monitoring is difficult, distributed logging and tracing is now needed.
- Testing, especially E2E, becomes more difficult. Your environment X, let’s say pre-prod, needs to communicate with all other services in environment X with clean data for tests to pass.
- Reporting and aggregations become difficult, data needs to be pulled from many services.
- Each service needs its own CICD pipeline, testing, environments ect.
Research and educate the team, at least have a shallow understanding of why these concepts exist and what problems they solve. Many of these points are not directly related to microservices but will be needed to implement microservices correctly.
- Event sourcing and event storming
- Event choreography and event orchestration
- The saga pattern and the circuit breaker pattern
- Eventual consistency and the CAP theorem
- Event Carried State Transfer
- DDD: Domain Driven Design and bounded context
- CQRS: Command Query Responsibility Segregation
What I had wrong
There are 2 concepts closely related and that is the Distributed monolith and the Modular monolith. The main difference is in how you structure your business logic and deploy. To sum it up, your architecture will fall within one of these categories:
- No boundaries between code, it is one application.
- Deployed as a single application.
- No physical boundaries.
- Loose Boundaries, sharing business logic between services.
- Services deployed independently (usually in the same repo, but can be multiple).
- Always has physical boundaries between them (think each service having their own compute or DB).
- Strict Boundaries, services might be sharing code(think utility functions) but not business logic.
- Services deployed together (usually in the same repo, but can be multiple).
- Might not have physical boundaries.
- Strict Boundaries.
- Services deployed independently.
- Always have physical boundaries.
The main difference between a distributed and modular monolith is that a distributed monolith shares business logic between services, have independent service deployments and each service has their own physical boundaries and resources. This is the worst of the 4 categories. Some signs of a distributed monolith include:
- Shared business logic like each service querying the authentication service DB directly for authentication.
- Long deployment times because each service needs to deploy independently.
Shoot for Microservice and fall on a Modular monolith if you have to. They are usually my go-to for any project that is small to medium with a single team. Modular monoliths usually live within a single project, where each folder might be a service.
Purists will argue that no code must be shared between services, but it usually happens that within a modular monolith utility functions are shared. As long as business logic stays within the boundaries of a service. They also share physical resources like DBs and are deployd together.
Use the right architecture for the job, consider the non-technical requirements first when deciding to use microservices. Does it fit with our company/project structure? Does the team have the required skills? Do I understand the business impact this will have?
There is nothing wrong with a monolith, you can still implement best practices and hit the targets set out by business. At some point your team will grow or usage pattern might change, then only consider the transition to microservices.