Why you should NOT start with microservices!

Patrick Wunderlich
Holisticon Consultants
6 min readApr 10, 2024
The “Trisselwand” is my favority mountain in Altaussee / Austra and is a peak of “Todes Gebirge” (Dead Mountains) which is a mountain range. So it’s a good metaphor for a Modulith.
Trisselwand [1] in Altaussee © Patrick Schalk

Microservices are cool, and also kind of trendy!

I have seen many projects that have built up a microservice landscape from the very beginning. They did this for good reasons such as good scalability and flexibility. It was also often used to break down the business context into small logical pieces from the beginning. As a result, development was (initially), fast, as were the CI/CD pipelines, which were completed after a few minutes.

In summary, all these things apply, and you may already know and agree with these aspects, so you may be wondering why I am suggesting that we should NOT start with microservices …

Downsides

  • The aspect of rapid development only applied to those cases in which we wrote new “green” code. We often had to refactor code and move it from one microservice to another because the code was better placed there.
  • Updating dependencies, e.g. for a new Spring Boot version is also annoying. In one project, we already had 90 microservices, with some of the individual services being very small, such as a service that only takes care of storing email addresses. Nevertheless, you must update 90 projects to use the latest Spring Boot version. For this task, we had our own cross-team mission that took several weeks to achieve.
    So, it was also quite expensive.
  • E2E Testing is also very difficult, as you need a stable test stage for this in which all microservice services fit together. (Keyword: Version Management)
  • The biggest disadvantage, however, was the infrastructure aspect. On the one hand, it is very hard to maintain the infrastructure landscape for all stages. On the other hand, if you use a normal Spring Boot service (without native compilation), each service has a large resource requirement that is only needed to run the service. This in turn is also quite expensive.

While all of this totally makes sense when we really need and use the scalability aspect, it’s just a waste of money for the phase before we need it, if we need it at all.

Back to Monolith?

Picture of a monolith.
Photo by Hulki Okan Tabak on Unsplash

One solution could be to go back and start with a monolith.

My Favourite quote regarding monolith vs microservices comes from Allard Buijze, the CTO & Founder at AxonIQ, a company providing frameworks and server components for large scale distributed applications:

Want to build microservices? Learn to build a decent monolith first!”

With a monolith, it is easy to refactor things in the beginning when it is not yet clear where the code should be placed. It’s also easy to maintain the infrastructure part as we only have one service that we provide. Updating dependencies is also quite easy.

But then we lose the ability to scale only individual parts of the application, and without proper modularization there is also a big risk that we end up with a lot of spaghetti code that is no longer maintainable or refactorable in the end.

AI generated picture of a monolith with lots of spagetti in it.
Generated with AI ∙ 3 April 2024 at 11:46 am

So, isn’t there some other solution?

Monolith -> Modulith!

Of course there is, and it is also nothing new or ground-breaking.
A Modulith, a modular monolith, also known as a deployment monolith, as Martin Fowler introduced in his 2015 article “Monolith First” [2].

The idea is to leverage the advantages of monolithic and microservices architectures by carefully organising the code base into well-defined, loosely coupled modules within a single application.

This has several advantages:

  • You only need to deploy one artefact, which makes the devops part very easy and cheap.
  • It is also easier to refactor the code as you can perform all refactoring in one application and use the features of the IDE. This is especially helpful in the beginning, when we are often not sure where things will end up.
  • In addition, you do not have to set up a separate test stage with all microservices to perform E2E tests. As it is a single deployment artifact, all E2E tests take place in a single application.

If you divide the code into modules, remember that each of these can later be outsourced to its own microservice. A good practice for this is to use a concept from Domain Driven Design called Bounded Contexts. This means that we try to cut the parts according to our business domain.

And there are other aspects that you should keep in mind if you want to extract modules later:

  • You should always start with a separate database schema per module, otherwise it is very difficult to separate them later. (e.g. Liquibase supports multiple schemas by defining a separate MultiTenantSpringLiquibasebean per module, see Christian Thiels blog post “Working with Liquibase in a multi-module Spring project” )
  • Try not to have cross-module transactions. Otherwise, when you extract modules later, this could lead to strange behavior because the transaction limit is suddenly different.

Example - Bank Application

For a fictitious banking application, for example, we could consider creating separate contexts for account, customer, transactions, etc.

In a microservices landscape, we might end up with something like this:

A deployment diagram with eight microservices and their associated databases.

With a modular architectural style, we can build this in the beginning instead:

It is important that you find a good way to loosely couple these modules.
This can be achieved, for example, by using eventing such as Spring Events. Additionally, it is good practice to have ArchUnit tests that ensure the architecture.

When we talk about modules, this does not mean that you must create separate Maven modules for each module. You can also simply start with different packages. This makes refactoring even easier.

If you use Spring Boot, there is a Library called “Spring Modulith” that can help you structure your application. It works package based, as shown in the illustration above, and it also supports verification of the architecture and simple integration tests of individual modules.

Extraction of modules

Later on, you can extract single modules. This can be necessary when:

  • You need a higher scalability for certain modules. For example, you have significantly more traffic with the transaction module than with the customer account.
  • If you have different teams working on the same module, it may be necessary at some point to distribute the software independently, as there are different release cycles.
  • Or if the modulith simply becomes too large and the runtime of the CI/CD pipeline becomes too long.

Extraction does not mean that you have to directly create your own microservices for each module; perhaps it makes sense to start with a second, smaller module.

Or if its already needed, we can create microservices directly for the extracted modules.

Conclusion

As always, this architectural style is not the solution for everything, and it is not always useful to use it. In addition, the architectural style does not take away from the fact that you have to think carefully about how to cut your modules.

But I think it is a very good way to focus on the essential parts of the application, the business domain, especially in the beginning of the project.

Example — Teaser

In my last project we used the mentioned modulith architecture style to build a marketplace that had several domains like product selection, checkout, and order. We also used CQRS/ES (Command Query Responsibility Segregation/Event Sourcing) and DDD (Domain-driven design) as design patterns.

In my next blog post (coming soon) I will give a practical demonstration of how it works.

[1] Trisselwand — The “Trisselwand” is my favourite mountain in Altaussee / Austria and is a peak of “Totes Gebirge” (Dead Mountains) which is a mountain range. So it’s a good metaphor for a Modulith, as it contains several peaks/mountains.

[2] ”Monolith First” — Martin Fowler , 2015, https://martinfowler.com/bliki/MonolithFirst.html

--

--

Patrick Wunderlich
Holisticon Consultants

Software Architect, Senior Consultant / Developer, Camunda Champion @ Holisticon AG