Breaking Down the Monolith

Going from one to many, how hard can that be?

Fernando Doglio
Bits and Pieces
Published in
8 min readAug 5, 2021

--

Photo by Damir Spanic on Unsplash

Monoliths are bad, we all know that — unless of course, you’ve watched 2001: A Space Odyssey — so why do we keep building them?

Why do we go through the exercise of building a monolithic application to only then, once it’s working, call it “tech debt” and decide that now we need to refactor it into a plethora of microservices?

Why can’t we just go microservice-first, like mobile-first for UI developers? Think about it.

Or rather, think about it while you read this article on why you’d want to break up that monolithic application you just published and maybe, just maybe, you will not make the same mistake next time around.

“Monoliths are bad”

I’m quoting myself, that’s how bad these things are.

Jokes aside, this requires a bit of context, because not all monoliths are bad (some of my closest friends are monoliths!).

Monoliths are bad when you’re building inside the “client-server” paradigm. Because if you’re building a desktop application, then it makes total sense to only create a monolithic-type application. However, when part of your logic is hidden and removed from the grasps of the user, then it makes little sense to have it coupled with the UI and all together with itself as well.

What do I mean by that? When your web app’s project looks like this:

your-mono-project
|
|- front-end
|- index.html
|- scripts.js
|- contact.html
|
|- back-end
|- api
|- main.js
|- db.js
|- sendemail.js
|- public
|- images
|- myface.png
|- logo.png

Then, I’m sorry to tell you, but you probably have a problem in your hands.

Why? I mean, the above folder/file structure is a bit silly, I know, but it also shows how right off the bat, your back-end code and front-end code are both living within the same project. That right there means that if you change one, you need to deploy both — ergo: bad.

Then let’s go a step further, within the back-end folder, you also have the db.js file and the sendmail.js file. These files reference two concepts that we tend to have in our applications and that by expanding on them we gain a lot of abstraction and freedom of movement in the future. What I mean is having db.js turned into a “Storage layer” and sendmail.js turned into a “Notification layer”. Otherwise you’re effectively getting married to SQL (potentially) and SMTP for the rest of your life. And trust me, unless you’re marrying an actual person, you want to stay away from that type of commitment.

Technology changes, business requirements evolve, if your app design doesn’t allow for that type of change, you’ll soon find that you can’t evolve as fast without a proper refactor.

Your app needs room to grow

Do you have kids? It’s just like that.

Oh, you don’t? Well then, the analogy probably went right over your head, didn’t it?

If you’re building something that is meant to last a long time, then you have to consider your app to be an organic being. It’ll need room to grow and adapt to the changing environment around it. If you’re only worried about the now, then you’ll close the door on that capacity to adapt.

Essentially what I’m trying to say here is that you have to work with abstractions during your initial design stage. And I know, we developers like to write code, but consider spending a few minutes drawing some boxes first.

A good components diagram can get you where you need to go

It’s not that hard and it doesn’t have to be very technical, consider this the sketch of your design. It can be done using a pen on a piece of paper, or something as sophisticated as Microsoft Visio, whatever you like. I personally prefer to use Diagrams.net given the integration it has with Google Drive, but that’s me, you do you.

Now spend 10 minutes thinking about what your app is going to be doing and start drawing a box for every functionality you identify. Is it sending emails? Then boom!, an “Email sending” box appears. Is it writing to a database? Then bam! A “DB” box appears. And keep doing that until you’ve covered all functionalities. Then look at those boxes and try to cluster them, group them up based on what they’re doing, you’ll identify resources or responsibilities by doing that.

Then consider that clustering as your initial design and validate it by thinking about coding them. Would they work if they were individual entities? Would it make sense to have each of these groups as individual projects? Or would you see the need to repeat some code? Like for instance, the connection to the database? Or perhaps a User class would need to be repeated between multiple services?

Take that problem and turn it into a solution: create a service for every repeated piece of code you find. Or rather, be smart about it and consider that every piece of code that you identify here is showing coupling between components. Do these components make sense if they were fused together? If so, then you’ve solved the problem, if not, then consider making the common code (the glue that binds multiple services together) a separate service. This is because repeating code is bad, that’s a big no-no, because the moment you need to update that code (due to an updated requirement, a change in the business needs or whatever) you’ll have to remember all the different places where you duplicated it. So instead, focus on abstracting away repeated code so that it can be accessed and used from multiple places.

By the end of this stage, you’ve created what I like to call the “Logical Components Diagram”. Essentially you’ve not been focusing on code that much, you’ve only been thinking about business requirements, technical requirements and limitations. That’s all and by the end, you have a diagram showing you the different boxes where you have to write your code. That’s it.

Turning logical components into code

It’s now the time to write code, yay!

Be careful though, If you now go back and write it all together (as part of the same project), you’re essentially breaking open those boxes. So instead, when you write your code write it following the “black-box approach”.

Every piece of code that needs to interact with another of these logical components should treat said component as a black box, something that can’t be looked into. At that moment you have no idea what the black box has inside, all you know is the type of functionality it provides and the interface it’s publishing (it’s API contract).

This will allow you to design your components from the outside, thinking about the needs they need to solve instead of the features you think they should provide.

Top-down design is a great way of making sure whatever you build is exactly what your users need. In this scenario, you are your own user, each service that needs another one is a user of the latter.

The benefits of breaking down a monolithic application into individual services are plenty and to be honest, you’ll become a better person through the whole process.

Alright, maybe that last part is a bit of an exaggeration, but you’ll definitely grow as a developer for it.

Going through the process I described above will provide you with:

  • A much higher understanding of your business needs and the technical decisions made around them.
  • A very scalable solution. Through the use of stateless microservices (which is essentially using REST on top of what I already described) you can then deploy as many copies as you need of one of your microservices to remove any bottlenecks your platform might be having when it comes to data processing or request handling of any kind. This is the cheapest way of scaling a component, and at the same time, it’s also the best and most reliable one.
  • A great way to identify weak spots in your architectural design. Is there a component that’s always crashing? Is the request taking too long? Which component is not responding on time? Those are all questions you can now easily identify and correct without having the noise of the rest of the platform working at the same time, on the same server.
  • A more reliable and stable platform. If a component crashes, there are ways to handle this without affecting the entire user experience. While before if your monolith crashed for any reason, the entire app would be down.
  • An easier to maintain codegbase. All of the sudden, you can have multiple teams working on different sections of your code without having to interact with each other. This simplifies a lot the development process.
  • Reusable components. If you design them well, some of the services you end up extracting from your monolith can be deployed as part of other projects or platforms you might be also working on, without any real change. Imagine for instance, having a notification service that sends emails based on whatever event it receives. This is completely generic and re-usable by any other piece of code.

I can probably go on for a while, but you get the gist of it: breaking down a monolithic application can provide you with a lot of benefits for you and your team.

And with that, you’ve successfully destroyed the monolith that was forcing you to couple your code, have a huge deployment orchestration script and live a life full of stress.

Now you not only have to maintain cleaner code but you also gained a lot from both, the design process and the final result.

Have you had to go through a process like this before? How long did it take and were you successful? Share your comments below!

Autonomous teams building together

Building monolithic apps means everyone works together, in one codebase, and on the same bloated version. This often makes development painful and slow as you scale.

But what if instead, you build independent components first, and then compose apps? Autonomous teams, building together!

Every team could work in their own codebase, develop and deploy their own features, and continuously collaborate with others to share and use each other’s components.

OSS Tools like Bit offer a powerful developer experience for doing exactly that. Many teams start by building their Design Systems or Micro Frontends, through components. Give it a try →

An independently source-controlled and shared “card” component. On the right => its dependency graph, auto-generated by Bit.

--

--

I write about technology, freelancing and more. Check out my FREE newsletter if you’re into Software Development: https://fernandodoglio.substack.com/