Access Employees

Migrating .NET monolith to microservices

Published in by Lucio Pelinson

Migrating .NET monolith to microservices Migrating .NET monolith to microservices

Introduction

I'd like to share a bit of my experience with this kind of architecture. First of all, keep in mind: there's nothing worse than this, and chaos is inevitable. Everything that once seemed simple and trivial is no longer that way.
Your trusty IMemoryCache will need to notify other applications, and other concepts, like IDistributedCache, will have to come into play. That reliable transaction? Maybe it won’t work anymore, and you'll have to give it up. You'll grow to hate infrastructure and network issues that used to go unnoticed. You'll start using Redis more often, layer queues on top of queues, and the environment will keep growing. Installation and maintenance will become increasingly complex.
So, think carefully before going down this path of no return. Below, I’ll list the main pros and cons of this architecture:

Pros and Cons

Pros:
  • Highly scalable and distributed architecture.
  • Deploy (GMUD) specific processes without impacting critical operations.
  • Allows multiple teams (squads) to work more independently and efficiently.
  • Flexibility to use different technologies can be a real advantage — switching technology can make hard tasks simpler and faster in some scenarios. (But beware of the infamous “Frankenstein system”!)

Cons:
  • Longer development time.
  • Much harder and more complex to manage the environment.
  • Integrated tests (TDD) become more complex and labor-intensive.
  • You'll need additional resources like message queues, cache servers, proxies, etc., which weren’t necessary before.
  • API versioning and service communication: previously, if you removed something from your monolith, the project wouldn’t even compile — the error was obvious. Now? That safety net is gone. And after a while, it'll be impossible to know who’s still using that old API version… until you shut it down and someone starts yelling. ("Oh, but you can document it and such..." Trust me, that’s wishful thinking.)

Recommendation

Many times, when I’m consulting with companies, simply splitting the current system into modules solves 90% of the problems. But when is microservices architecture truly the best option? In my opinion, when you genuinely need to scale an application, there's no way around it. But keep in mind — we’re talking about thousands of simultaneous requests, or huge bottlenecks in specific processes with many users working at the same time. In that scenario, I can say with full confidence: this architecture is a game-changer.
I often hear:
"But what if one day I *need* to scale? Wouldn’t it be better to build it this way from the start?"
My answer is clear: no. Start with a monolith. There are patterns and best practices that help structure your system in a way that makes it easier to split later. Some practices I strongly recommend:

Best Practices for a Monolith

Modularize your system:
Write your monolith in a modular way. Whenever possible, separate your business rules. If you ever change your mind — either to merge or split modules — it’ll be much easier, and refactoring will save you.
Database design:
Group tables by module. If you can isolate them into separate databases, even better. If not, at least use schemas to separate them — this will make your life much easier later.
I always hear:
"But Lucio, it’s not possible… the customer, the product, the user hierarchy... we use them across several modules!" Yes, that will happen. A simple and effective way to deal with this right away is using views. For example, create a `customer` view in the module's schema, containing only the fields used there. Never access other modules' schemas directly.
Module dependencies:
Whenever possible, expose an interface in your module to represent a dependency. Inject the class from another module that implements this interface. Be very careful not to reference other modules indiscriminately. If coupling gets too tight, re-evaluate your module boundaries. In a monolith, this is much easier to spot and fix.
With these simple precautions, scaling your application later will be much easier.
← Return to start