What Are Microservices?

5 min. read

Microservices describes a cloud-native architectural approach to software development that structures an application from loosely coupled services in communication with each other via APIs or messaging protocols. Each service is autonomous and self-contained and runs a unique process. As developers seek to build scalable and resilient applications, microservices have become increasingly popular.

Microservices Explained

Microservices, also known as microservices architecture, is a type of software architecture used in cloud-native application development. Applications engineered by this design comprise small, independent and loosely coupled deployable components that together provide the capabilities of the application.

Each service in microservices architecture performs a distinct business function and communicates with other microservices through well-defined interfaces, mostly using RESTful APIs.

Departing from the monolithic application developed as a single unit, microservices allows developers to build with modules they can independently develop, test and deploy, which accelerates time to market. As the decoupling nature of microservices allows developers to push new code and functionality more frequently than they otherwise could, modern applications are able to keep pace with evolving customer needs.

More than three-quarters of businesses have pivoted to microservices, replacing their monolithic applications hosted by individual web servers for containerized, cloud-native applications distributed across a cluster of host servers.

From Service-Oriented Architecture to Microservices

Service-oriented architecture (SOA) has been around since the early 2000s as a way to build large, distributed systems by decomposing them into smaller, loosely coupled services. Microservices architecture emerged as a natural evolution of SOA.

The concept of microservices was introduced by Fred George in a 2011 workshop on software architecture. George had been trying to solve scalability issues with SOA while working on an e-commerce site and came up with the idea of building small, autonomous services.

Microservices architecture took SOA principles of service orientation and refined them for modern cloud-native applications. The coarse-grained services of SOA became fine-grained, granular “micro” services, which made them highly efficient and provided the flexibility to match a technology stack to a given service. Microservices architecture even reduced the communication load by replacing cumbersome SOAP APIs with lightweight options, such as REST APIs or message queues.

Microservices soon gained popularity among software architects, and companies like Netflix, Amazon, The Guardian and Spotify began adopting this architecture.

Microservices architecture is the basis for the benefits associated with the modern, cloud-native application.
Figure 1: Microservices architecture is the basis for the benefits associated with the modern, cloud-native application.

Benefits of Microservices

Microservices provide a framework for building cloud-native applications that can adapt to changing business requirements. The myriad benefits arise from the architecture of the application.

 

Agility

 

Microservices architecture lends itself to independent development and deployment. Unlike monolithic applications, where changing a line of code involves updating an entire application, developers can modify or replace services without affecting the distributed system. The ability to deploy individual services makes it easy to add new features or roll back changes to an application.

 

Scalability

 

Scaling an entire application isn’t optimal. With microservices, only the components that require scaling are scaled. Developers can address an individual service as needed, which ultimately facilitates better performance under heavy loads, the efficient use of resources and lower infrastructure costs.

 

Choice

 

In microservices architecture, the cloud-native application doesn’t share a common stack and database. Developers can choose the tools they prefer and the technologies to meet distinct requirements of individual services.

 

Integration

 

Developers can write microservices in any language — and connect them to microservices programmed in any language. What’s more, microservices can run on any platform, which makes them available to integrate with legacy systems.

 

Code Reuse

 

Microservices architecture allows developers to build modular services they can reuse across applications. By working with reusable components, programmers reduce development time and improve the quality of code as they invest in a "write once, reuse often" culture.

 

Fault Tolerance

 

Microservices architecture promotes resilience. With services designed to operate autonomously, the failure of a single service rarely shuts down the application, as tends to happen with monolithic applications.

 

Collaboration

 

Microservices architecture enables teams to work on different services simultaneously, which translates into faster time-to-market. While developers make decisions without needing to coordinate with other teams, microservices also promote cross-team collaboration, as each team is responsible for developing and maintaining a part of the whole. This can lead to better alignment with business goals and more efficient use of resources.

 

Continuous Iteration

 

The application built with microservices is built to evolve. Developers can quickly deploy core microservices as a minimum viable product and upgrade the application as teams complete additional services. New technologies can be incorporated into the design as they emerge. The microservices-based application remains in process, continuously moving toward theoretical perfection.

When to Use Microservices

While container-based microservices provide many benefits, they’re not always the right application architecture to choose. When making software engineering decisions, think about your goals for the application, as well as development hurdles and needs you foresee in view of the application’s lifespan. Microservices work best with complex applications. Scenarios to consider using microservices include:

 

Large Applications

 

If you're building a large and complex application, microservices will allow you to divide the application into manageable pieces, making it easier to develop, deploy and maintain.

 

Timeline Complexities

 

Microservices architecture can accommodate independent services with different development rates. Even if a service sees an unexpected delay, the project can continue without global implications to the application development timeline.

 

Frequent Updates

 

Microservice architecture is ideal for applications that will require frequent updates, as independent services allow developers to modify the module instead of the application.

 

High Scalability

 

If your application needs to handle a high volume of traffic or needs to scale rapidly, microservices are essential. This is particularly if you need to scale specific parts of the application, rather than scaling the entire application.

 

Multiple Teams

 

If you have multiple development teams working on the same application, microservices will help you maintain agility and efficiency. Each team can work on their microservice, using the technology stack that works for them, without worrying about the rest of the application.

 

Decentralized Architecture

 

If you want to build an application with a decentralized architecture, microservices are autonomous and can be deployed in different locations, even among different cloud service providers.

 

Hybrid Cloud

 

If you're planning on a hybrid cloud architecture, where some services will continue running on-premises and others will run in the cloud, microservices will help you manage the complexity of the application.

API calls representing client requests routed by API gateway to the endpoints of the internal microservices
Figure 2: API calls representing client requests routed by API gateway to the endpoints of the internal microservices.
 

Building and Deploying Microservices-Based Apps

Microservices architecture requires careful planning. Certain technologies and practices common to the production environment enable developers to effectively develop, maintain and operate microservices-based applications.

 

DevOps

 

DevOps practices, including CI/CD, are essential to the architectural approach of microservices. Unlike monolithic applications, microservices are inherently complex distributed systems with numerous moving parts and independent tech stacks. This complexity requires frequent collaboration between development and operations teams to ensure that components are seamlessly integrated.

DevOps practices provide the necessary collaboration, communication and automation tools to effectively bring teams together throughout the entire software development lifecycle.

 

Continuous Delivery

 

Continuous delivery goes hand-in-hand with microservices, allowing developers to release software updates frequently and reliably by making use of infrastructure automation tools, such as continuous integration servers, deployment pipelines and automated testing frameworks to streamline the CI/CD process.

Continuous delivery is especially important to ensure that each service can be updated and released independently of the other microservices.

 

REST

 

Microservices communicate with microservices — and most do so within web applications — which makes REST complementary. REST, or Representational State Transfer, is an architectural design pattern for building RESTful APIs, which allow services to communicate via HTTP in standard formats, such as XML, HTML and JSON. But REST APIs are foundational to microservice-based apps for several reasons.

REST APIs are light-weight and platform-agnostic, meaning they provide a standardized interface that enables microservices to communicate, regardless of their underlying technology. Because requests contain the information needed to complete the request, REST APIs don’t require context stored on the server. They can handle large volumes of requests without compromising performance, and services in a REST-based microservices architecture can evolve independently, efficiently communicating in a stateless manner.

 

Containers

 

While microservices give teams the option to choose their service’s language and framework, working with various languages in the same CD pipeline poses challenges. Containers abstract away the variance between services, as each microservice becomes a self-contained unit packaged with its codebase, database and dependencies. The CD pipeline, now homogenous, can perform consistent testing of each container.

Services are able to interact without interfering with each other when separated by containers, and once deployed, containers provide a lightweight, portable runtime environment that allows services to function consistently across platforms. Tools like Docker and Kubernetes are widely used to manage containerized microservices.

 

Kubernetes Orchestrator

 

An orchestration tool like Kubernetes can abstract the underlying infrastructure and automate container management, deployment and scaling across multiple servers. Its extensibility also enables developers and operators to use their preferred open-source and commercial software tools, reducing the manual work of container management.

 

Serverless

 

Serverless computing is another option for deploying microservices. Serverless architectures use functions as a service (FaaS) platforms to create even smaller units of deployment and scale on demand. Though serverless architectures can increase vendor dependencies, they offer reduced operational cost, complexity and engineering lead time.

 

Microservices Best Practices

Designing a microservice architecture requires careful planning and consideration. To build successful microservices-based applications, developers should observe the following best practices:

  • Domain-driven design: Domain-driven design (DDD) is a design approach that focuses on the business domain and the application's behavior. It helps developers split the application into smaller, more manageable components, making it easier to build, deploy and maintain.
  • Service boundaries: When designing a microservice architecture, it's essential to define clear service boundaries. Each microservice should have a well-defined responsibility.
  • Small services: Keep services “micro,” focused on a single responsibility. Losing sight of this foundational principle will sacrifice manageability.
  • API design: Microservices communicate through APIs, so use consistent, scalable and secure APIs that restrict data access to authorized applications, users and servers.
  • Decentralized data management: Microservices applications require a variety of storage and database options. Each microservice should have its own datastore. This approach helps you avoid data inconsistencies and allows you to scale each microservice autonomously. You want the development team to choose the database for each service to ensure it best suits their project.
  • CI/CD pipeline: Implementing CI/CD will help you find and fix bugs quickly, which is especially valuable with multiple codebases to manage in a microservices architecture.
  • Intentional resilience: Protect the application from dependency-failure shutdowns. Don’t use remote procedure calls (RPCs) between microservices, if possible, and incorporate features like circuit breakers to arrest cascading failures.
  • SOP: Develop standards operating procedures that define coding conventions, directory structures and communication protocols. Following a set of standards will result in consistent and manageable microservices.
 

Adopting Microservices

eBay, Etsy, Uber — countless enterprises have dismantled their monolithic applications and refactored them into microservices-based architectures to capitalize on scaling advantages, business agility and financial wins.

Organizations that plan to transition to microservices should first adopt DevOps, which will set you up to manage the complexities you’ll encounter. On a basic level, anticipate the stages outlined below when mapping out your project.

 

Identify the Business Capabilities

 

The first step in migrating to microservices architecture is to identify the business capabilities or features your application needs to support. This will help you define the scope of your application and inform your decisions about which capabilities should be prioritized for development, as well as how those microservices should be designed and integrated with one another.

 

Decompose the Monolithic Application

 

Most organizations use domain-driven design or feature-based decomposition to decompose their monolithic applications.

Having identified the application’s business capabilities, define the service boundaries for each microservice, ensuring that each microservice has a distinct, well-defined responsibility. You’ll want to map dependencies between the business capabilities, datastores and external systems. Based on the bounded contexts and dependencies, define the microservices that will replace the monolithic application.

Each microservice, focused on a single business capability, should have clear interfaces. Review how the data entities are accessed and, finally, consider how to partition the data to reduce dependencies between services.

 

Define Service Interfaces

 

Implement the service interfaces for each microservice, ensuring that the interface reflects the microservice’s sole responsibility. You can use different techniques, such as RESTful APIs or messaging protocols, to define the service interfaces.

 

Implement and Test the Services

 

Depending on your requirements and expertise, choose the programming languages and frameworks to implement the services. Iterate on the design as needed, including testing the new interfaces, communication protocols and datastores.

 

Containerize the Services

 

Once you have implemented and tested the services, you’ll want to containerize them using container technologies, such as Docker or Kubernetes. Containerization will enable you to deploy and manage the services independently.

 

Automate the Deployment and Orchestration

 

Automate the orchestration of the services using tools such as Kubernetes or Docker Swarm. In addition to efficiently streamlining the deployment of services, automation through Kubernetes or Docker will improve the reliability and availability of the application. Either platform can detect when a service instance fails or becomes unresponsive and take action to remediate the issue. Kubernetes, for example, can restart failed instances or reschedule them to other nodes, while Docker can automatically migrate the failed container to another node.

 

Monitor and Manage the Services

 

Decomposing a monolithic application is not a one-time process. It requires maintenance and updates as the needs of the application and its users evolve. Monitor the new microservices and track key metrics, such as response time and resource utilization.

 

Securing Microservices

Highly distributed, cloud-native microservices applications introduce security complexities. Instead of a single entry point, they come with dozens, if not hundreds, of potential points of vulnerability — and each must be secured. APIs and code dependencies represent but two sources of risk in the expanding attack surface of the modern application.

 

Web Application and API Security

 

Modern applications consume input from a range of sources — standard web requests, mobile device API calls, cloud events, IoT device telemetry communication, cloud storage, etc. What’s more, a single client’s web request (i.e., north-south traffic) can spawn hundreds of API calls between internal microservices (i.e., east-west traffic).

The complexity of API-centric web applications require scalable, flexible and multilayered strategies that work for any type of workload in any type of environment or cloud architecture. Securing the frontend web interface of the cloud-native application isn’t enough. Cloud-native applications require application-layer protection for cloud-native APIs. Web application and API security (WAAS) is essential.

 

Code Dependencies and Software Composition Analysis

 

Open-source software components make up approximately 70% of cloud-native applications. While this expedites development, many open-source packages and their dependencies contain vulnerabilities. As well, each version of a dependency-driven OSS package can change critical functionality. Without full visibility, vulnerabilities go undetected.

Standalone software composition analysis (SCA) tools surface open-source risks too late in the development lifecycle, which causes a backlog of vulnerabilities that can’t always be resolved. Separate tools for SCA and IaC security result in noisy alerts without context and knowledge of interconnected risks. Because gaps are inevitable without runtime and workload coverage, it’s best to secure cloud-native applications with integrated cloud-native security.

 

Code-to-Cloud CNAPP

 

Identifying and prioritizing critical risk of the entire cloud-native application, a cloud-native application protection platform (CNAPP) integrates multiple types of security to deliver comprehensive, code-to-cloud protection — cloud security posture management (CSPM), cloud workload protection, cloud infrastructure entitlement management (CIEM), Kubernetes security posture management (KSPM), infrastructure-as-code security, WAAS, SCA and more.

Cloud security leadership exploring how best to secure the rapid development needs of applications employing cloud-native technologies, such as containers, microservices and serverless functions, should consider adopting a CNAPP.

 

Microservices FAQs

All cloud-native applications are microservices applications, but not all microservices applications are cloud-native. Microservices architecture can be implemented on-premises or in the cloud and does not necessarily require cloud-specific technologies or tools.
Microservices communicate via APIs, and an API gateway is often used as an intermediary layer, especially as the number of services in an application grows. Located at the outer edge of your microservices, the API gateway acts as a proxy to manage all ingress traffic, providing a single point of entry and routing all requests.
A service mesh is a dedicated layer of infrastructure applied to a microservice-based system that enables developers to separate and manage service-to-service communication within a microservices architecture. Service mesh technology can handle things like service discovery, load balancing and traffic management, freeing developers to focus on writing code rather than managing the infrastructure.
Since microservices are distributed systems with multiple components and services, logging and monitoring play an essential role in preserving the overall health and performance of the system. Each microservice generates a log, which needs to be aggregated and analyzed to identify issues in the system.
Stateless microservices don’t maintain state information between requests. Designed to be autonomous, they don’t rely on previously stored data to handle incoming requests. Stateless microservices are easier to manage and scale but may require additional complexity to maintain data consistency across multiple requests. Stateless microservices are often used for simple and independent tasks, such as data validation or authorization, where stateful operations are not required.
Stateful microservices are a type of microservice that maintains the state of an application. This means the microservice has access to and can modify data that is persisted between requests, which can include user session data, database connections or other stateful information. Stateful microservices can provide benefits such as improved performance, better data consistency and reduced complexity of application design. They do, however, require additional management and can be difficult to scale horizontally.
If a domain represents a problem to solve — as it does in domain-driven design (DDD) — the domain model is the model that implements the solution to the problem.
Bounded context represents a set of functional features, a domain model, within the boundary of a domain. In other words, just as a subdomain is a segment of the domain, a bounded context is a segment of the solution. A microservice, then, is a bounded context — but not all bounded contexts are microservices. Bounded contexts, in fact, aren’t necessarily isolated from each other.
Distributed tracing is a technique that enables developers to trace a request through multiple microservices and identify any issues in the request flow. Distributed tracing tools allow developers to track the flow of requests through the system and identify any bottlenecks or issues in the request flow.
Microservices architectures are designed and implemented using microservices pattern language. Pattern languages include patterns for event sourcing, data management, communication and more.
Service discovery patterns help applications and services find each other in a distributed microservices environment. Service instances change dynamically due to scaling, upgrades, and service failures, and these patterns provide discovery mechanisms to cope with the transience. Load balancing may use service discovery patterns by using health checks and service failures as triggers to rebalance traffic.
Adapter microservices patterns translate relationships between otherwise incompatible classes or objects. An application that relies on third-party APIs might use an adapter pattern to ensure communication between the application and the APIs.
Strangler application patterns help manage the process of refactoring a monolithic application into microservices by slowly replacing parts of the monolith with microservices.
Backend-for-frontend (BFF) patterns identify how the data is fetched between the server and clients. The BFF inserts a layer between the user interface and the resources that the interface calls on, which allows developers to create and support one backend type per user interface, using the best options for that interface. This, in turn, improves frontend performance by tailoring resources to the needs of the interface.
Entity and aggregate patterns classify data in meaningful ways. For example, an e-commerce site might use the entity pattern to represent individual products and the aggregate pattern to represent orders, which are collections of products ordered by a buyer.
Caching is an important part of many microservices applications, as it can help to improve performance and reduce the load on the backend. Most cloud service providers (CSPs) provide a managed caching service to their customers.
Object storage is integral to many microservices applications, as it allows for the storage and retrieval of large files and data objects. Most CSPs offer a managed object storage service that can be used to store and retrieve objects for microservices applications.