The Importance of Microservices Architecture for Modern Applications
So what has changed? Amazon embraced agile software development practices and started migrating to microservices architecture.
Amazon today is a collection of loosely coupled, autonomous services, supported by “two-pizzas” teams – an engineering team of 6-8 people who have full ownership of one or few microservices (specific application features). Agile teams handle the entire product lifestyle of one service – gather customer requirements, create feature roadmaps, design, develop, test, deploy and operate them. That’s exactly how Amazon makes up to 50 million deployments per year.
This post will further explain what is microservices architecture, how it translates into multiple business benefits and what are the best practices for testing and implementing it.
What is Microservices Architecture?
Image Credit: TechCrunch
Microservices architecture (or microservices) is a particular way of developing software, where applications are structured as a collection of autonomous services.
In other words, large complex products are broken down into individual mini-apps (microservices) that are responsible for one specific business function, e.g. social media logins or e-commerce basket. Microservices can be loosely coupled or deployed independently of one another – that’s up to you to decide. Loosely coupled means that changing one service does not require updating other services at the same time.
The main idea behind microservices architecture (MSA) is to create independent software components that do one thing well. Each microservice should be cohesive: it has a well-defined purpose e.g. track shipping history or process payments. Microservices should be designed around business processes, not horizontal layers such as messaging or data access.
Each microservice has a bounded context: a specific boundary within a domain where a particular domain model applies. The challenge, however, is to define appropriate boundaries for each microservices. That’s where domain-driven design (DDD) comes to the fore. The key principle of DDD is to develop a new architecture that closely aligns all your business processes and use cases with respective code artifacts. It’s a framework you can employ in complex domains to establish the optimal software architecture design:
- Step 1: Domain Analysis.
- Step 2: Bounded Context Definition.
- Step 3: Definite entities, aggregates, and services.
- Step 4: Identify exact microservices.
The advantages of following domain driven design methodology are multifold. Your application will be built around the concepts of the domain, meaning that it will indeed account for what the domain needs, instead of focusing only on certain requirements e.g. being UX-friendly. You end up with a product that meets the needs of users in that specific domain.
Your application is more flexible. Every microservice is modular and encapsulated, enabling rapid changes and improvements continuously. Additionally, each microservice can be built using different programming languages, databases and software environment. You can use most suitable technology for the task at hand, i.e. microservices in Java can co-exist with .NET microservices, written in C++ or C# without any issues. To communicate with one another, microservices use language-neutral APIs. Unlike the platform used by a microservice, these should be standardized.
Ultimately, clean application boundaries allow developers to focus on what matters the most – finding the best solution to a certain problem. They produce code that is more reliable, avoid duplications and understand where and why certain integrations are crucial.
Production-ready microservices are independently deployable and easy-to-replace in case of failure or glitch. This means that you can deploy frequently and rapidly to meet the market need. At the same time, your business architecture becomes more resilient and agile.
If you want to dive deeper into the tech specs, Microservices.io has a detailed wiki description of the common microservices patterns for architecture, infrastructure, deployment, communication and API vs. microservices explanations.
Microservices vs. Service-Oriented Architecture (SOA) vs. Monolithic Architecture: The Differences
Image Credit: IBM
Like Amazon, most legacy products were originally developed as monolith applications – complex, chunky, multi-tier systems, built, tested, packaged and shipped as a single unit. All of the business logic ran in a single app server.
The problem? Monolith applications are becoming operational mammoth in the modern age for the following reasons:
- Scaling is complicated and expensive. You need to scale the entire application even if you want to fix a localized bottleneck.
- Complex deployments. You are losing time-to-market, as new code deployments need careful orchestration, scheduled downtimes and maintenance windows. You cannot ship new functionality or updates as quickly as a couple of times per week or even per day.
- Single codebase is often hard to assess for new team members. It requires careful maintenance and meticulous updating to avoid breaking any dependencies. Bloated code bases can hamper IDE performance, and require longer deployment push times if the services are developed in the cloud.
- Single programming language. The application is written in one programming language that might be outdated. Introducing a new-gen tech stack is challenging.
- Testing times are slow. Due to the complexity of monolith architecture, testing can take a lot of time. Automated testing techniques, e.g. regression testing are also hard to implement as they require building up a history of a large suite of regression tests. Otherwise, a small change may create a large negative ripple effect on the entire product.
Service Oriented Architecture (SOA) emerged as an alternative to monolith applications in the early 2000s. Essentially, this software architecture also involves the deployment of services – an application that handles a certain process and delivers that service to other components via a communication protocol.
Service-oriented applications typically include a strict hierarchy of services. First come, the topmost level services that control the overall flow of activities and invoke one or more businesses services, depending on the users’ action.
The second level consists of services that fulfil a specific business task, e.g. validate a user’s email address. The third level includes data-access services – each of them handles tasks related to reading/writing data to data-storage areas.
Although both SOA and microservices architecture assume the use of loosely coupled services, there are some major differences in terms of how these architectures function and what goals they accomplish.
Microservices vs. SOA: Quick Comparison Chart
Services can vary in size and range from single-feature applications to full-feature products or sub-systems.
One service is designed to accomplish one task perfectly.
Relies on Enterprise Service Bus (ESB) as a middleware component. Uses messaging protocols such as AMQP, MSMQ and SOAP as primary remote access protocols.
Has an API layer between services and services consumers. Uses homogenous protocols including REST and simple messaging (JMS, MSMQ).
Promotes and enhances component sharing between different application components. Relies on multiple services to fulfill a request.
Minimizes component sharing through “bounded context”. Each service is designed to run independently from others and require no knowledge of the underlying implementation or architecture of other microservices.
DevOps and CD
A systematic change requires modifying the monolith.
A systematic change stands for creating a new service.
Shared between services.
Independent for each service.
Microservices rely on containers to be self-sustaining and self-running.
Let’s dwell further on the last point. To understand how to develop microservices architecture, you will also need to learn more about containers.
Why Containers and Orchestration Frameworks are Essential for Microservices Architecture
Containerizing a service means that it is packaged in a “box” with everything it needs to function properly – code, runtime, system tools and libraries.
Containers enable developers to simultaneously build and ship different microservices; integrate them with other systems and automatically orchestrate them using predefined rules and processes. This translates to higher productivity, better code quality and faster time-to-market. The essential container tools we advise to take a look at are Docker, rkt, and Microsoft Containers.
However, containers will bring little value to your product lifecycle without proper orchestration, as your team will struggle to keep tabs on all things. Managing a distributed and auto-scaling container-based system requires DevOps expertise and modern tools.
Consider using a cluster scheduler/container orchestration system such as Kubernetes, Docker Swarm or Apache Mesos to automate microservices deployment and other tasks such as:
- Scaling up or removal of containers for balancing load evenly across the infrastructure to achieve seamless microservices scalability.
- Regulating the redundancy and availability of containers.
- Resource allocation between containers.
- Load balancing of service discovery between containers.
- Overall health monitoring.
Microservices and containers are a dynamic duo that enables faster development and fault-prone provisioning and configuration. They allow companies to deliver new features faster while improving quality and resiliency of the final product. For instance, teams using Docker containers reported a 13x increase in frequency of software releases. Early adopters like Airbnb, Disney, Dropbox, GE and others have reported a 75% drop in development lead times after shifting to microservices.
Moving from monolithic to microservices architecture often requires transformational changes within the organization. Apart from refactoring the existing application, you will also need to adopt new strategies for testing microservices, especially if you are still relying on manual testing alone.
Key Strategies for Testing Microservices
The challenge with microservices testing is that you will need to develop tests covering a large number of different applications, and account for the numbers of dependencies between those services.
However, a complete microservices testing strategy does not differ much from testing any other type of application. It all boils down to choosing the optimal testing methods for each individual service (done by the “two-pizza team”) and dependencies between them. So, consider the following:
Unit testing: create automated test cases that will give you instant feedback on how the implemented piece of code behaves in isolation. Developers who wrote that code should also write and run the test scripts.
Integration testing is used to analyze transactions between different layers of integration code, and any other external components that they integrate to, e.g. data storage, other microservices, etc.
Component testing isolates one service to probe it for quality and performance using test doubles for any services that it invokes. It gives you more insights into the overall product performance.
End-to-end testing helps overcome the possible gaps between different microservices. It helps to verify the correctness of message exchanges between the services; ensure proper configuration of additional network infrastructure such as proxies, load-balancers, etc. In general, it helps ensure that the whole system meets all the business requirements and works up to specs.
Microservices performance testing is done against service-to-service calls and a particular service in isolation to identify any possible issues.
Ultimately, your testing strategy should be aimed at detecting and solving problems early in the production cycle. This means that all of your tests should be optimized to deliver fast feedback if you want to deploy new quality features frequently just as the leaders in the MSA space.
The Ultimate Business Benefits of Implementing Microservices
Microservices enable modularity. Think of this approach as an assembly line – instead of building the entire product at once from scratch, your development teams work on standalone, complete and elastic components of a larger application. Each service can be modified, removed or upgraded independently, without hampering the entire application performance.
Today, 68% of companies are already using microservices in production and development. And here’s why you should catch up:
Each service runs as an independent component, meaning you can scale up a single function on-demand, without scaling the entire application. Your product becomes more agile and can handle the shifting usage patterns while offering you a reduced total cost of ownership, especially if you are using microservices architecture in the cloud. Similarly, you can optimize on-premises server usage and performance by deploying business-critical services on multiple dedicated servers to delimit resource usage of different software components.
Continuous Delivery and Continuous Deployment
Microservices are the backbone of a continuous delivery model, where development and testing are merged into one unified process. With this approach, you create a new environment in which the code is continuously developed, debugged and deployed to a production-like environment. IT organizations that have used CD, report the following improvements:
- 21% increase in new software and services delivered;
- 22% improved quality of deployed applications;
- 19% increase in revenue;
- 50% fewer failures.
Microservices architecture enables better fault isolation. If one component fails, it will not bring down the entire product in a cascading avalanche of malfunctions. By deploying microservices using Docker – a popular container tool – you can add an additional layer of resiliency: a new container will be started automatically whenever one container fails. Your product’s full capacity and redundancy can be restored in minutes, not days.
Microservices code is organized around business capabilities. New engineers don’t need to spend days on learning the entire application logic; they just need to understand how a single microservice functions and what business outcome it should deliver.
For instance, a subgroup of developers and admins can be assigned to build the frontend microservice architecture in Java, while another one will work on a backend Node.js microservice. A delay or a problem with shipping one microservice will not create a bottleneck for another team and impact their performance. Furthermore, this increases the autonomy of individual teams, as ideas no longer require coordination or approval with a wider IT delivery group.
Various app components are isolated. If a security problem arises in one section, it will not affect other areas of the application. Microservices security practices, however, should account for the increased usage of APIs and ports per app – the “backdoors” intruders may try to leverage.
Absence of Vendor/Technology Lock-in
Microservices can be developed in different programming languages, using new technology stack. Meaning, you have more room for experimentation and testing. You can also choose to upgrade only critical app components with new stack, tools or technology.
The Challenges of Implementing Microservices
Clearly, the benefits of using microservices are outweighed by certain challenges businesses will need to account for.
Microservices architecture consists of a lot of moving parts. As already mentioned, there are a lot of dependencies between different components and those need to be tested effectively using new approaches. Refactoring across service boundaries also presents certain difficulties.
Lack of Unified Governance
MSA allows you to leverage different languages and frameworks. However, you may also end up dealing with just too many different technologies, making your system hard to maintain. You may want to impose certain project-wide standards, at least for cross-cutting functionality to avoid “technology anarchy”.
Each microservice comes with its own data storage unit. As a result, reaching data consistency may become a problem. Plan and account for this early on.
Maintaining a complex MSA project requires a mature DevOps culture and CI/CD expertise. Otherwise, this type of architecture can work against you in terms of productivity. You will need to evaluate whether your current team has the right skill sets to support such distributed system.
Network congestions and latency
Improper API design can sabotage your product. Make sure that no chain of service dependencies grows too long to avoid additional latency. Consider using asynchronous communication patterns to balance chatty APIs.
Service updates can happen independently at any time. Make sure that your design accounts for the backward and forward compatibility.
To get a better sense of the challenges associated with migrating from monolith to a robust microservices architecture, read this compounding post by Martin Fowler.
Two Prominent Examples of Microservices Implementation
To further illustrate the full spectrum of benefits you can obtain with microservices architecture, let’s take a look at two case studies from companies you are well aware of.
Back in 2008, Netflix was a monolith application, prone to error and downtime. A single small failure in code could result in hours of downtime that negatively impacted the company’s revenues. Plus, Netflix was also struggling to meet the growing demand of their services on multiple platforms including Xbox, Wii, and mobile. Building and provisioning resources from data centers wasn’t speedy enough. So, the company chose to migrate to the cloud to achieve instant resources provisioning and began their transition towards microservices architecture.
As of today, Netflix application is capable to:
- Handle about two billion API edge requests per day through an API Gateway.
- Support around 500 microservices.
In turn, this new architecture has resulted in:
- A 1,000x growth in monthly streaming hours within 8 years.
- Service expansion to 130 countries.
- Nearing the goal of four nines of service uptime.
- Faster development and deployment of new features and services.
30+ independent development teams, working simultaneously on different releases without interfering with one another, now support microservices architecture. The company managed to achieve a massive improvement in agility and overall software development productivity.
The retail giant was always popular with consumers on Black Fridays. In fact, so popular that their website has crashed two years in a row under the pressure of avid shoppers. Walmart e-commerce solution couldn’t handle up to 6 million page views per minute due to outdated monolith architecture.
In 2012, the company has started their replatforming journey towards microservices architecture, and their results so far are rather prominent:
- Conversions increased by 20% almost overnight.
- No downtime during Black Friday and Boxing Day.
- Mobile orders increased by 98%.
- Walmart managed to save 40% of the computing power and witness a 20-50% total cost savings overall.
Evidently, microservices allow large companies to gain agility and new tech capabilities to meet the growing customer demands. The adoption of microservices architecture isn’t without certain trade-offs though. It will not occur rapidly if you need to refactor and replatform a legacy monolith system with complex dependencies. To support the new microservices architecture, you will also require a team with substantial DevOps skills to set up proper orchestration, improve automation during the release stage and guide you towards embracing the continuous delivery model. That’s exactly where Infopulse specialists can step in as either an integral part of your team or as external consultants. Get in touch with us to learn more about our DevOps consulting and implementation service.