Breaking down a monolithic application to microservices – where to start?

Nebojša Pongračić, Tech Lead .NET

Tech

11.02.2020.

featured image

Since breaking down a monolithic application to microservices is a popular topic in tech right now, we thought we could write a blog post series about this topic.

A word of warning though – I am primarily a C#/.NET developer, so my examples will reference utilities in the .NET ecosystem. However, you can easily find a similar solution in your {insert_language_here} of choice.

So, let’s dive in.

As most talks about microservices go, first you need to start boring your audience with some theory about defining monoliths and microservices so I’ll do just that. Here’s a nice picture of a monolith:

Monolith structure diagram

Monoliths are not inherently evil; in fact, they are a good starting point and you should probably start designing your applications as monoliths first.

It’s when you get big, or perhaps you have a large codebase – you have large teams maintaining the thing and when you need to release something (a “quick fix”) to production, it takes a few centuries and you need to go through tons of approval and release managers and retesting. THAT might mean you have a problem.

But even then, you might not need microservices.

Still think you need microservices? Perhaps you have a new business requirement that requires you to process significantly more data than before and your current system just can’t handle the load?

You’ve come to the right place. So, what are microservices?

The microservice architecture is the distributed architecture of a system that consists of several isolated modules, working together and forming a greater whole. Here’s a nice picture of a microservices architecture:

Microservices architecture

So, the goal is to get from a monolith to this architecture depicted on the nice picture above. A word of warning though: microservices are tough.

What you gain in deployment and development simplicity (each team can develop and deploy its own microservice independently, with its own technology stack), you lose in visibility, error handling and debugging. Visibility means you need to know what happened and where, and that’s a lot harder to know when you have hundreds of microservices.

You need to be mindful of this problem while planning and designing your services and hopefully have some good tools that can help you overcome these issues.

So, now that the boring theory part is over (and most of the talks about microservices run out of time and stop here), we can focus on breaking down monoliths to microservices.

Break it down

So, you have a monolith. May I suggest, if you can, you take a good chunk of your planned time to take a snapshot of your current system and break it down into separate parts, much like we did with our monolith example, and we saw that there is a business process that handled orders, so we called that Orders Service and grouped all the ordering functionalities into this particular service. We also did that for the Products Service and so on.

These parts are called bounded contexts or domains and you can read more about them here. (Also just google it. Every link I found was on Medium and they’ve started putting paywalls and read limits, shame on them).

It’s a very hard thing to do and your bounded context will surely change over time.

You can try to find a part of your system that is completely integrated so it’s less difficult to “break away” into a separate functionality. Or you can just jump in and pick one area and stick with it.

Once you start developing, whatever you do, DON'T do a big bang rewrite of your entire system. Do it incrementally and over time, breaking away piece by piece until the monolith is no more.

To aid you in this process there are some nice design patterns like the Strangler pattern and the Anti-Corruption Layer pattern.

You can read more on the microservice patterns here.  

Strangler Pattern

Helpful link here

So, the strangler pattern is probably your weapon of choice when tackling breaking away specific functionalities from your monolith.

Basically, the idea here is that you gradually replace some specific functionalities of your old system with your new applications and services. As you replace them bit by bit, the old system is no more (you’ve “strangled” it). It is now replaced with your shiny new microservices system. A nice illustration of the strangler pattern phases:

Strangler pattern phases illustration

You put facades that mimic your old functionality but which are actually communicating with your new system. A good example of this would be replacing an old legacy SOAP web service that is exposed to some parts of your legacy system (or external users) with a new REST web service.

You could put an API gateway in front of the old service that redirects the old SOAP xml requests and transforms them into your new REST API calls. Nothing has changed from the legacy system perspective but it’s actually communicating with your new system.

This approach might not be possible for every part of your system but you’d be surprised in how many cases you can implement it.

Make sure to take into account that you reflect changes back into your legacy system if you need to, so you don’t break things that you haven’t migrated yet.

For example, you’ve created a new service that has its own database and you’ve successfully implemented the strangler pattern for most of the functionalities, but not all (e.g. there is a batch job or something that generates reports that you didn’t have the time to migrate yet).

Don’t be afraid to maintain and update the old structures as well and duplicate functionality until you migrate all the functionality away from the legacy system.

Anti-Corruption Layer pattern

And while you’re doing your strangling and stuff, you might find that you need an anti-corruption layer pattern. Helpful link here.  

Basically, what this layer does is that it translates requests that one subsystem makes to the other subsystem. A nice illustration of it working is here:

Diagram of anti-corruption layer pattern

And while we’re on the subject, the API Gateway example that we used in our Strangler pattern scenario works here, as well (the translating SOAP to REST part). Yay!

Be sure to check out our next article explaining how to actually go about developing your microservices - find it below.

RELATED

19.02.2020.

Breaking down a monolithic application to microservices - real world examples and tips we use in our .net world

For our next steps, I wanted to give you a few real-world examples of designing and developing microservices that we use in our .NET world.

Read more