Monolith vs SOA vs Microservices vs Modular Monolith: My Take
I have worked with monoliths, SOA, microservices, modular monoliths, and combinations of these architectures.
After seeing all of them in practice, my conclusion is pretty simple:
KISS. Keep it simple and stupid. Don't over-engineer your solution.
I don't believe there is one architecture that is always the right answer.
For a small company, I would start with a monolith with strict service/module boundaries. The important part is the boundaries. Just because everything is deployed together doesn't mean everything needs to be tightly coupled.
The advantage is that you can keep the domain separation without immediately introducing network calls, distributed transactions, service discovery, complicated deployments, observability problems, and all the other operational overhead that comes with microservices.
My SOA experience
I worked on a big project around 2009 where SOA was the architecture everyone was pushing.
Looking back, there was a lot of overhead.
XML everywhere felt like overkill. You had the SOA headers and bodies, security around the messages, and other infrastructure that made development much more complicated.
One of the biggest problems was that we didn't even have proper developer testing environments.
That experience stuck with me.
Architecture can look great on a diagram, but the real question is: does it make developers' lives easier and does it solve an actual problem?
Microservices: great boundaries, expensive communication
I like the idea behind microservices.
Take a fintech system and split the domains into things like:
- Card
- Account
- Transaction
- Fraud
- Identity
- Master data
- User
- Fee
On paper, this looks very clean.
But then you have to look at what actually happens.
Imagine an API partner wants to create a card for an end user.
The flow might become:
- Onboard the user.
- Identity service performs KYC.
- Card service checks whether the user exists.
- Check the user's KYC level.
- Master data determines whether that KYC level is allowed.
- Create an account.
- Call the fee service to charge the card creation fee.
- Create the card.
- Create the embossing entry.
- Queue a webhook and notification.
If every one of those steps becomes a network call to another service, you have created a very chatty system.
Now you're dealing with network failures, retries, timeouts, distributed tracing, data consistency, orchestration, BFFs, and other infrastructure just to perform what is logically one business operation.
This is where I start questioning the architecture.
What I prefer
I would rather have something like:
CardsController -> CardService.CreateCard()
And inside that business operation, call the required components directly:
CardService.CreateCard()
UserService.GetById()
IdentityService.GetKYC()
AccountService.CreateAccount()
FeeService.ChargeFee()
CreateCard()
CreateEmbossingEntry()
The important difference is that these are collocated calls, not network calls.
I still want clear domain boundaries.
I just don't automatically want every domain boundary to become a separately deployed service.
That's where I think a modular monolith makes a lot of sense.
Modular monolith gives you an escape hatch
The thing I really like about a modular monolith is that you can establish the boundaries early.
You can have a Card module, Fraud module, Identity module, Fee module, and so on, while keeping them in the same application initially.
Then, if one of those modules eventually needs to become a separate service, you already have the boundary.
For example, Fraud in a fintech company is a great candidate.
Maybe fraud eventually needs:
- Independent scaling
- Its own team
- Different technology
- Stronger isolation
- Independent deployments
- Different security requirements
- Failure isolation
At that point, extract it.
I don't see service extraction as a failure of the monolith. I see it as an evolution of the architecture.
So when should something become a service?
I think all of the usual criteria can make sense:
- Independent scaling
- Independent deployment
- Team ownership
- Security or isolation requirements
- Different technology requirements
- Failure isolation
- Different operational characteristics
The key is that there should be a reason.
Don't create a service just because a box on an architecture diagram looks cleaner.
What would I choose today?
If I joined a small fintech company today, I would start with a monolith with strict service/module boundaries.
I would optimize for development speed, the team's skills, and the operational capacity we actually have.
Then, as the system grows, I would move something into a separate service when it is actually needed.
Maybe that's Fraud.
Maybe it's Transactions.
Maybe it's something else.
The architecture should evolve based on the needs of the system and the organization, not because microservices happen to be fashionable.
For me, the biggest lesson from working with all these architectures is still the simplest one:
KISS. Keep it simple. Don't over-engineer the solution.
A distributed system isn't automatically a better system just because it has more services.
Bring us your technology challenge.
Complex problems rarely fit inside one technology. Tell us what you are trying to solve and we will tell you how we would approach it.
Discuss a Technology Challenge