Both terms “monolithic” and “microservices” refer to software architecture approaches used in designing computer applications. Often they are compared, and microservices are presented as a solution to the deficiencies of monolithic architecture, and this is mostly true. However, both approaches have historical underpinnings that date back to early distributed systems, but can be thought about as distinct ways to solve software design problems given limited resources and technology. Modern technology, especially virtualization and containerization, grants new capabilities that elevated microservices as a significant engine for efficiency in the cloud computing world, but because many legacy systems were built before these technologies, they relied on single-tiered application approaches.
Monolithic architectures are single-tiered applications, including all the data access and user interface in one package for a single platform—think back to buying software in a box at a local store. For home users, this may seem manageable. For enterprise level applications, these monoliths can be problematic to debug, update, and develop new releases, especially while maintaining smooth business operations. Microservices architecture, on the other hand, is not a single application, but several small services working together, contributing to the whole user experience. A microservices approach and monolithic approach seem to be on opposite ends of a spectrum, but the idea of microservices is a response to the unwieldy mess that many monolith codebases have become.
Oftentimes, it is advised not to develop microservices-based applications from the ground up. It is better to begin with a central code base and migrate functionality to microservices. While at first skipping the monolith altogether seems like a plan for potentially saving huge chunks of engineering time. But, by developing initial iterations into one application, teams actually save time, money, and management headaches because the code base is centralized and the inherited microservices dependency structure is not needed. In these beginning stages of development, this is simpler. Eventually, as services within the monolith demonstrate stability, they can be cleaved off into their own microservice, and removed from the monolith code.
Those microservices are now independent, capable of scaling as needed. Cleaving off the code which is most suited for scaling makes a good first microservice candidate. Individual teams can then be assigned to manage those microservices with dramatically less difficulty, and better efficiency.
Compared to monolithic code bases, microservices architecture approaches use virtualization and containerization as a vehicle for service delivery. Service functionality is contained within microservices, and tens, even hundreds of unique microservice can comprise the total functionality of a monolith application.
Microservices are small apps designed to fulfill a single purpose. Each microservice is principally in charge of a single “concern” within the larger system, perhaps surrounding a business line, and operates like a black box, communicating with the outside through REST APIs, event streaming, and message brokers. Microservices architecture commonly aims at breaking down larger applications into smaller services, each acting as their own service component to other microservices, or larger programs, giving information upon request, but also creating an interworking set of independent services.
An independent microservices approach to deployment affords organizations several capabilities over traditional monolithic approaches:
● Code is partitioned into microservices, encapsulating just that code specific to the concern of the component, updates can be more easily made, new features more easily added, all without disturbing other application code.
● Universal communication through APIs allows microservices to be written in whichever language is necessary, convenient, or available, and still be able to interact with other microservices.
● Microservices can be individually scaled to fit the demands on the system with ease.
Conceptually, microservices are an idea born out of service-oriented architecture (SOA). But, technologically, microservices are an evolutionary step from SOA, thanks to the supporting technology of virtual machines, containers, and container orchestration software.
Monolithic architecture may have competition with today's microservices based cloud companies, but there are still advantages, such as ease and rapidity of development, and less cross-program communication concerns.
● Straightforward Software Development — Monolithic architecture approaches are more straightforward than microservice development, and can be helpful for developers who are initially coding a “first draft” of their application. As functional divisions in the monolith show stability, they can be removed from the monolith, APIs can be developed for those functions, and then the code can be optimized for a microservices environment.
● Less Latency — Generally speaking, monolithic software requires no outside program calls, or at least a vast majority of program calls are local, with few outside calls to databases or other services that may not be local. Whereas, microservices architecture is divided and relies on inter-service communication via APIs, which in effect add to latency, and potentially reduce application performance.
● Simpler Inter-functional Communications — Because the functionality of monoliths resides together, implementing workflows that involve multiple functions is usually simpler, for example, implementing a purchase order in combination with fulfillment and shipping requests may share the same local data and therefore be easy-access.
The main advantage of microservices comes from its flexibility in subdividing and compartmentalizing large programs into the basic functional units, and then encapsulating those functions in virtualized containers provisioned with the exact resources that container needs. This allows modern server technology to scale these services on-demand, quickly, by simply making copies of those service containers when the existing services reach their capacity. This style grants developers several advantages.
● Market Readiness Increased — Microservice architectures support agile deployment reducing development cycles, ultimately bringing services to market faster.
● Improved System Scalability — With containerized microservices, as demand increases, additional servers can be deployed quickly and easily.
● Heightened Resilience — Independent services are fault isolated, if one service fails, it can be destroyed without impacting any other services.
● Enhanced Development and Deployment — Microservice-based applications are easy to deploy, localize complexity, increased developer productivity, simplified debugging and maintenance, and enable smaller more agile development teams.
● Future-proofed Applications — In many ways, microservices are future-proofed apps: innovation is easier to adopt in virtual environments; microservice size reduces experimentation risks; microservices can be built around business lines improving developer and business user alignment.
Microservices and service-oriented architecture are similar and can be contrasted with monolithic approaches. Both microservices and SOA divide large code bases into smaller components. However, SOA is older than microservices architecture, and can be considered the parent or big brother of a “services” approach.
In 1997, enterprise java bean (EBJ) was introduced which provided a means to make “small” services, to encapsulate reusable code. But EBJ was limited to Java, and could not communicate with outside systems. The evolutionary solution was SOA, which employed an enterprise service bus (ESB) to connect and manage enterprise services. ESB have their limitations: 1) the ESB is a single point of failure, meaning if the ESB fails, the system services won’t be able to communicate, effectively taking down the service; 2) the ESB, like public roadways, can become congested and lead to decreased system performance. However, out of SOA came SOAP (simple object access protocol). SOAP would define the foundation of a microservices philosophy, “do the simplest thing possible,” and utilize object methods via HTTP.
Over a decade later, in 2011, the term “microservice” was coined, which became the enabler for those who espoused DevOps philosophy and continuously deployed systems. Microservices adopted RESTful communications over SOAP, which does not scale well nor support error handling. REST, on the other hand, is highly flexible, and inter-operable with various systems over the internet.
When should a development team use microservices? The time to use a microservices approach may not be at the beginning of development. While microservices do impart many advantages that successful applications use, the technologies pose other developmental concerns beyond developing the underlying application logic. At the beginning stages of development, especially during MVP stage, it is helpful to develop in a monolith method, in order to minimize the overhead of delivering services. Once areas of functionality become stable within the main code base, then they can be separate from the monolith into their own microservices. Shifting to microservices is useful when scaling will be critical, or when disparate services use other languages.
Migrating from monolithic code to microservices will create structure changes within the application code. The two main refactoring strategies are to implement new functionality as services, or to extract services from the monolith to stand on their own. These changes follow some basic principles that will help developers adapt to microservice deployments.
● Map Dependencies and Optimize — Dependencies are one of the key limitations in monolithic applications. Understanding exactly the dependencies will go a long way to helping map out microservices.
● Decouple Dependencies — Refactor code to decouple the mapped dependencies. This will also mean creating APIs from remote user interfaces. Decouple capabilities not code.
● Optimize Deployment for CI/CD — Microservices are very useful in CI/CD lifecycles.
● Split Sticky Capabilities — Like decoupling, teams should attempt to split sticky capabilities from monoliths. Sticky capabilities are those functions within monoliths that are not a well-defined domain, potentially with many dependents.
Choosing an approach to software architecture design depends on the end use. The following table with factors for comparison, should help to align decisions over monolithic or microservices with the end need.
|
Monolithic Architecture |
Microservice Architecture |
Agility/Flexibility |
Monoliths are unfriendly to acquiring new technologies |
Microservices welcomes new services, even written in other languages |
Deployment |
One time deployment, with upgrades, updates, and patches |
Microservices deploy and rollback individually |
Development |
Teams group together and develop in massive chunks |
Multiple autonomous teams can work independently on their assigned microservice(s) |
Reliability |
Monoliths can have several single points of failure |
Microservices are built with redundancy |
Scalability |
Limited to vertical scaling |
Microservices are designed for high scaling |
Security |
Secure data processing and transferring is easier at the system level |
Communication between services via API gateway raises security issues |
Technologies |
.NET, Java, PHP, or Ruby, Python/Django |
DevOps, Docker, Kubernetes, Lambda |
Testing |
Possibility of end-to-end testing |
Each component needs to be tested individually |
Updates |
Updates are dependency laden, updates are released periodically |
Microservices allow fo CI/CD workflows |
Monolithic application deployments follow a traditional approach, that is to release the application on a web server like Apache, Oracle, or IBM’s. Microservices can be deployed on on-premise servers, or in the cloud.
● Single Virtual Machine — This is a traditional approach to microservices. Deploying all the services onto a single virtual machine can decrease deployment time, and save infrastructure costs.
● Containerization of Services — Containerized deployment models utilize container and orchestration platforms like Dockers and Kubernetes, the two more popular. These platforms create the containers that wrap services individually, and orchestrate resources to meet their needs. These containers can be easily replicated if more capacity is needed.
● Serverless Deployment — Serverless deployments use languages that support stateless and serverless, such as Node.js, Python, and Java. These are exceptionally cost effective.