The topics of Microservice architectures and distributed systems have swirled around virtual watercoolers and their implementation put forth as best practice for my entire Engineering career so far. I’ve taken it for granted that Tech companies are migrating towards these practices. However, after hearing Rob Zuber’s talk, Building Microservices Without Abandoning Your Monolith at the inaugural Seattle CTO Summit, it made me realize that there’s more to the story and that it’s time to learn more.
Monolithic vs. Microservices Application Architecture
To begin with, the important problems to solve involve maximizing software delivery velocity and its components: maintainability, extensibility, and testability. As Chris Richardson describes in his book, Microservices Patterns, an application goes from “monolithic to mud” in its deployment architecture as its size increases. In general, organizations optimize by adopting various degrees of a monolithic or microservice architecture depending upon their growth stage and organizational inclination. A simplified summary of the advantages and disadvantages of either approach for large applications is below, with green being the better approach.
Again borrowing from Chris Richardson’s “Microservices Patterns”, the difference between these two approaches is described in the following application example.
Monolithic Architecture: Keep It Simple, Startup! (KISS)
At inception, applications are typically designed as a single deployable unit. It’s simple and easy to understand at the outset and scaling concerns mostly centered around capacity and availability.
“Despite having a logically modular architecture, the [example] application is packaged as a single WAR file and deployed on Tomcat. It is an example of the widely used monolithic style of software architecture, which structures a system as a single executable or deployable component. If the application was written in GoLang, it would be a single executable. A Ruby or NodeJS version of the application would be a single directory hierarchy of source code.”
As the application grows in size and complexity, it gets harder to maintain and understand, development velocity slows, and failure risks increase. While there are ways to optimize the system, even the best teams find that they need to move to some form of a Microservices architecture.
“On the one hand, a disciplined team can slow down the pace of their descent towards monolithic hell. They can work hard to maintain the modularity of their application. They can write comprehensive automated tests. But on the other hand, they cannot avoid the issues of a large team working on a single monolithic application. Nor can they solve the problem of an increasingly obsolete technology stack. The best that a team can do is delay the inevitable. In order to escape monolithic hell they must migrate to a new architecture: the Microservice architecture.”
Microservices: Solving for “Monolithic Hell”
Richardson’s definition of Microservices is grounded in concepts derived from The Art of Scalability(3) and the Scale Cube, its three-dimensional scalability model. In sum, while both X- and Z-Axis scaling improve application capacity and availability, Y-Axis scaling minimizes development and application complexity by splitting an application into its component services.
The latter provides the definition for Microservices, “an architectural style that functionally decomposes an application into a set of services.”
As shown in the diagram of the Microservices architecture version of the example application below, many services correspond to the modules that described in the monolithic example. What’s different is that each service and its API is very clearly defined (application code can be found here).
Got Microservices? You Need Distributed Systems
Okay, so you have a large application migrating to a Microservices architecture. You need to have a distributed computing system in order to reap its benefits. A distributed system is defined on Wikipedia as a system whose components are located on different networked computers, which communicate and coordinate their actions by passing messages to one another(4) .
From IBM, “computers that are in a distributed system can be physically close together and connected by a local network, or they can be geographically distant and connected by a wide area network. A distributed system can consist of any number of possible configurations, such as mainframes, personal computers, workstations, minicomputers, and so on. The goal of distributed computing is to make such a network work as a single computer”(5).
How Do All-Kinds-of-Computers-Everywhere == One Computer?
In a word, Communication. This is a whole other topic that is beyond the scope of this article but successful communication in a distributed system requires these three high-level parts:
- Nodes, also known as processes, agents, or actors, are the individual entities in a distributed system. Individually running their own operations, they vary in nature from hardware devices to a software processes (6).
- Connection via message protocols and software
- Successful coordination and security
For a quick overview of distributed system concepts, components, and terminology, check out Kyle Kingsbury’s Introduction to Distributed Systems.
- Richardson, Chris. Microservices Patterns: with Examples in Java. Manning Publications, 2019.
- “Microservices Pattern: A Pattern Language for Microservices.” Microservices.io, https://microservices.io/patterns/index.html.
- “The Art of Scalability.” The Art of Scalability, http://theartofscalability.com/.
- “Message Passing.” Wikipedia, Wikimedia Foundation, 8 Nov. 2019, https://en.wikipedia.org/wiki/Message_passing.
- IBM Knowledge Center, https://www.ibm.com/support/knowledgecenter/en/SSAL2T_8.2.0/com.ibm.cics.tx.doc/concepts/c_wht_is_distd_comptg.html.
- Joshi, Vaidehi. “Many Nodes, One Distributed System,” https://medium.com/baseds/many-nodes-one-distributed-system-9921f85205c4