Scaling Applications Part 5 — Splitting the Monolithic Application

Hughie Coles
5 min readJul 2, 2020

--

Photo by Sean Pollock on Unsplash

This is the 5th in a 7-part series on how to scale applications. The series will cover common scaling patterns, as well as their pros, cons, and caveats. *Note — This structure may change as the series goes along.

Setup

In the series so far, we’ve replicated the application layer, replicated the data layer, and split the data layer. This has allowed us to handle much more traffic and much more data as our site get more popular.

In this post, we’re going to talk about Service-Oriented Architecture (SOA). This type of software architecture will give you the following advantages:

  • You can scale your application more granularly
  • You can deploy parts of your application separately
  • You can use different languages and technologies for different components

We’re going to start off with the simple application from part 2, which consists of a single application layer speaking to a single data layer.

Problem

Previously we’ve been dealing strictly with our ability to reply to requests. Obviously this is incredibly important as your application grows, but at some point you’re also going to worry about scaling your development and release flow.

Part of what drives traffic is the value that you bring your customers, and as your application grows in size and complexity, you’ll find that it becomes harder to maintain your development speed. More developers sharing a more complicated code base will hinder your ability to move quickly. The issue with this is that when you’ve got a large monolithic application, you’ve got many parts of your application coupled via code or via the database. This means that if you change a section of your code, or a database table, you may affect some completely “unrelated” functionality. The only way to protect yourself from this is to increase your testing and decrease your release cadence. I’d like to be able to change my invoicing functionality without potentially affecting my user logins.

Solution

The solution is to start moving toward services. Specifically, splitting your application into services based on functionality and domain. If you’ve got a store, you can split your application into a billing service, a shipping service, a user service, a communications services (think email, sms, etc…).

This is called a Service-Oriented Architecture (SOA). The idea is that if you split your application into services, they can be developed, managed, deployed, and scaled independently of each other. Each service can be written in its own programming language that is better for solving the service’s problem, store data in the way that best suits its use-case. Communication typically happens via either a simple http or tcp-based messaging system, or via a queue or service bus of some sort. The latter of which greatly decreases coupling.

A simple SOA architecture

Alternative Solutions

Modular Monolith

In a modular monolith, you have a single application. However, you break functionality down based on domain similarly to SOA. The difference is that it resides in a single process. You can develop separately and package the different sections as things like NPM packages or nuget packages (or whatever your language/platform support). They can reside in the same repository or in their own repositories. The key is that they allow the package to develop independently. Here is a great article on how Shopify uses this approach.

Microservices

Although I cover Microservices in the final part of this series, I’ll touch on them hear. Microservices is SOA on steroids. Rather than break your application up into a few services based on domains, you’re breaking your application down into dozens, hundreds, or even thousands of very specific, very granular services. This amplifies all benefits and drawbacks.

Pros

Because these services run separately, and coordinate with each other via messages, they aren’t coupled directly in code, and can therefore be developed and released separately. This allows you to keep velocity high, and not be delayed by the work and releases of other devs and units.

SOA also cuts down on unanticipated interactions between seemingly unrelated functionality. If you choose to have separate databases for each service, this can eliminate schema-level coupling as well.

You also gain some fault tolerance. A single error may not bring down your entire application, but rather just a portion of it, allowing your application to limp on until the problem is addressed.

Finally, you can scale at a more granular level. If sales skyrocket, you can scale out your shipping service, but leave your user management module at its current level.

Cons

The main disadvantage of the SOA approach is complexity. Your testing becomes more complicated and relies more heavily on integration tests; your releases require more coordination and automation; and the communication between different parts of your code requires more thought and more tooling. Logging also becomes a nightmare and monitoring becomes much more important, since you have many more services that can fail.

You’ll also see some performance degradation as you move through more services over a network. If your order needs to hop through 2 or 3 levels of services, the fanout will affect response times.

Considerations

At this point you’ll have to decide what you’re optimizing for. If you’ve got a small and/or inexperienced team, the extra knowledge required and the overhead will kill you. However, as your platform and team grow, splitting things will allow you to cope with this growth.

This can also increase costs. Having multiple smaller databases and applications can cost more than a single large application.

When making these decisions, there is no universal right or wrong answer. You have to determine what’s best for your product, your team, and your company.

Summary

Service-Oriented Architecture (SOA) allows your the split your application into multiple services. This can allow you to design, develop, release, and operate parts of your application independently from other sections, allowing you to handle growth of your team, your application, and your capacity requirements.

This comes at the cost of more complexity, more cost, and performance.

These are decisions that should be made on a case-by-case basis depending on your use case and your resources.

--

--

Hughie Coles

I’m an EM, full-stack developer, speaker, mentor, and writer. I blog about software development, software architecture, leadership, and culture. @hughiecoles