What are microservices? The pros, cons, and how they work
Microservices are a popular software design architecture that breaks apart monolithic systems. A microservice application is built as a collection of loosely coupled services. Each microservice is responsible for a single feature. They interact with each other via communication protocols such as HTTP.
What are microservices
The microservice architecture is a software development methodology that helps with the design, development, and maintenance of large-scale applications.
The approach has grown in popularity with the adoption of modern software development practices such as the Agile methodology, DevOps culture, and the Software Development Lifecycle (SDLC) framework. Microservice applications are made up of several single-concern, encapsulated services that provide data to each other via high-level interfaces, such as REST APIs.
While being composed of several independent services, microservice applications still act as standalone applications. End-users experience them the same way as monolithic applications, and they have no idea whether the underlying architecture is a monolithic code base or split up into microservices.
SOA vs microservices – What’s the difference?
Microservices follow similar principles to the Service-Oriented Architecture (SOA) software design methodology. SOA has no official standards, but the de facto principles are defined in the book SOA: Principles of Service Design, authored by Thomas Erl.
They are as follows: 1. Standardized service contract (services follow a standardized description) 2. Loose coupling (minimal dependencies) 3. Service abstraction (services hide their internal logic) 4. Service reusability (service structure is planned according to the DRY principle) 5. Service autonomy (services internally control their own logic) 6. Service statelessness (services don’t persist state from former requests) 7. Service discoverability (services come with discoverable metadata and/or a service registry) 8. Service composability (services can be used together)
Although SOA and microservices follow similar principles, their relationship is debatable. Some developers emphasize their similarities and consider microservices as a subtype of SOA. Others stress their differences and claim that each solves a different set of problems.
The main differences between the SOA vs microservices architecture are summarized in the following comparison table:
SOA (Service-Oriented Architecture) | Microservices architecture | |
---|---|---|
Scope | enterprise-level | application-level |
Focus | on the reusability of services | on the decoupling of services |
Communication | messaging middleware, e.g. ESB (Enterprise Service Bus) | API (Application Programming Interface) |
Data management and storage | shared data layer | decentralized data management and storage across services |
Service size | ranges from small, single-purpose application services to large enterprise services | small, single-purpose application services |
Messaging protocols | supports several messaging protocols and methodologies, e.g. SOAP with WSDL, AMQP, HTTP with REST, etc. | supports lightweight protocols with flexible communication architectures, e.g. HTTP with REST |
Management style | Focused on common processes and standards | Focused on decentralized decisions, collaboration, and automation |
Characteristics of microservice architecture and design
Let’s review the main characteristics of the microservices software architectural style.
Service APIs
Component-based architectures aim to implement the DRY principle by providing shared functionality via reusable components. Componentization can be implemented via libraries or services — the microservice architecture uses the latter approach:
- Libraries are deployed together with the application, stored on the same machine, and called from functions (in-process calls).
- Services are deployed independently, run on their own infrastructure, and are called via web service requests or remote procedure calls (out-of-process calls).
Both approaches have their own pros and cons. In-process calls are faster and less resource-intensive; however, with microservices, you don’t have to re-deploy the entire application whenever there’s a change to one service, and each component can be developed and maintained independently.
Cross-functional teams
Traditionally, software development teams are organized according to functionality — e.g. there are design, frontend, backend, database, and other teams.
However, the microservices software design methodology assigns developers to cross-functional teams where each team develops one or more microservices, and each microservice is developed and maintained by just one team from start to finish. This approach also requires a cross-functional skill set within the team.
Ownership over the entire product lifecycle
Cross-functional teams are responsible for the entire lifecycle of a microservice, which includes everything from UX design to frontend and backend development to the deployment and maintenance of the service.
In other words, microservice application developers work with products, instead of projects that they pass on to the next team when they’ve completed their part of the development process.
Decentralized development
As microservices are treated as standalone entities, developer teams can find the best tools and technologies for each one.
For example, they could decide to build each microservice in a different programming language (however, they could also use the same language if they want), and the services will still be able to communicate with each other.
Decentralized data management
Like allowing teams to choose the best programming languages and developer tools for each microservice, data management is also decentralized in the microservice architecture. Microservices manage their own data. They can either create separate databases in the same database management system or use different database management systems.
Automated workflow
Even though automation is not required for developing microservices, this architecture can lead to complex systems that are hard to maintain and deploy manually. As a result, microservice applications tend to make heavy use of automation software, such as CI/CD (Continuous Integration/Continuous Delivery), automated testing, automated deployment, and other tools.
Design for failure
As a microservice application is composed of several microservices, the application architecture needs to be designed in a way that ‘tolerates’ failures. This means that it must keep operating when any component of the microservice becomes unavailable, and the reaction to the failure needs to be as graceful as possible.
This requirement adds extra complexity to microservice applications, which can be mitigated with the adoption of new methodologies and continuous monitoring with a reliable monitoring tool that can detect or even predict the failure of each service in real time.
Microservices vs monoliths
While the microservices architecture can be a good choice in many cases, especially for larger applications developed by distributed teams, you might still be better off with a well-structured monolithic application.
Here are some of the pros and cons of microservices against the monolithic architecture:
Pros of microservices
- independent services have greater agility
- freedom of choice of software and hardware
- helps teams follow the SOLID design principles (e.g. the Single Responsibility Principle)
- supports distributed development
- can be easily used together with modern technologies (e.g. containerization and cloud computing)
- faster time to market
- better scalability
- faster development cycles
- isolated services are easier to debug and maintain
Cons of microservices
- extra complexity that smaller teams are not always prepared for
- messaging between the services has a performance overhead (e.g. network latency, message processing, etc.)
- hard to maintain without automation and advanced project management practices such as Agile
- adopting the methodology has a steep learning curve
- additional security issues (e.g. transaction safety needs extra care)
In other words, what we gain from the simplicity of single-responsibility microservices, we can lose on the complexity of the entire application, the performance overhead, and the extra security needs of the messaging system that connects the services.
And while the microservices architecture allows each team to choose which programming language and platform to use, team members also need to collaborate much better in order to manage the whole lifecycle of their microservice.
Monitoring microservices can help with the additional complexity of distributed applications, but it needs to be planned and implemented with care and expertise — see our recommendations and best practices.
How to implement the microservice architecture
Whether you’re starting from scratch or refactoring an existing monolithic application, there are many things you need to pay attention to when implementing the microservice architecture.
Here are the most important ones:
Follow Conway’s law when structuring your application
Conway’s law is a well-known principle of software development management, defined by Melvin E. Conway in his paper called ‘How do Committees Invent?’ back in 1968. It goes:
> >“Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.”
This is frequently used as a rule of thumb when organizing microservice teams and structuring microservice applications.
If there are communication issues within the organization, e.g. two teams are working on overlapping goals, you can easily end up with a number of smaller monolithic architectures connected to each other via APIs, instead of single-responsibility microservices.
Avoid ending up with accidental monoliths
When software development teams try to split up an existing monolithic application into microservices, they sometimes unintentionally create accidental monoliths.
Ignoring Conway’s Law can be one of the reasons, but you can run into other architectural issues when refactoring your code base. For example:
- a microservice has more than one purpose
- the data is not properly separated
- the messaging system still includes communication logic that should have been moved into the microservices
To avoid creating smaller monoliths instead of microservices, it’s best to move toward the new architectural style in smaller steps. For instance, you can start with refactoring less important functionalities that can be decoupled from the core application without much risk.
Refactor your monolithic application with service objects
One way to gradually refactor an existing monolithic application into microservices is by creating service objects and identifying the data that each object inputs and outputs.
Service objects are objects that execute one single business action — these will be later your microservices. They don’t include any data but only perform the operations.
The data used throughout the operations is modeled by another class that’s called from the service class. In this way, the data and application layers are separated, and the data can be changed without affecting the business logic.
Design smart endpoints and dumb pipes
The concept of smart endpoints – dumb pipes addresses the additional complexity of microservice applications that comes from the messaging mechanism (pipes) that connects the individual services.
A well-designed microservice architecture keeps the messaging system as simple as possible and avoids smart pipes (e.g. the use of an Enterprise Service Bus) because these solutions create more or less centralized systems, which can lead to accidental monoliths.
As microservices keep all the business logic and most of the communication logic (smarts) within the service, the application doesn’t need a message bus, and the communication can be managed by simpler, asynchronous messaging applications such as RabbitMQ (see below).
How to deploy a microservice application
There are a number of solutions you can choose from when deploying a microservice application, depending on the complexity of the architecture and your business goals, budget, and other factors. You’ll need to consider many things, including scalability, reliability, maintenance and running costs, deployment tracking, and more.
The easiest way to deploy a microservice application is to run all the services as processes from one single machine, which can be either a rented server or located on-premise. However, this solution is only recommended for smaller applications.
To improve scalability, you can deploy your application to more than one server. For example, you can deploy one microservice to one machine, or if it makes sense, you can use more servers but run more than one microservice from each server.
While running microservices as processes is possible, maintenance and dependency management can become time-consuming and prone to errors beyond a certain complexity.
As a solution, you can package each microservice as a container that includes all of its dependencies. Using containers for deploying microservice applications keeps the services isolated, makes dependency management easier, and simplifies the deployment process.
However, these days, you don’t necessarily have to run your own servers, as you can deploy microservices to serverless platforms running in the cloud. If you choose this deployment option, your cloud provider takes care of everything from scaling to maintenance to container management. However, this solution also has its own drawbacks — for example, as it uses pay-as-you-go pricing, you can end up with surprisingly high bills.
5 tools to deploy a microservice application
The following five tools can help you with deploying microservice applications.
If you are interested in more, take a look at our collection of the best DevOps tools, too, all of which can be used for developing and deploying microservices.
1. Docker
Docker is the most well-known containerization tool in the market. It makes it possible to deploy microservices to any platform, operating system, or server environment. Microservices packaged as Docker containers are also portable — you can move them from one platform to another without having to worry about managing dependencies.
To learn more about the benefits of containerization, check out our article on the reasons behind Docker’s popularity.
2. Kubernetes
Kubernetes is a container orchestration platform developed by Google. It can come in handy when you have to deploy lots of containerized microservices packaged with Docker or any other container platform. Kubernetes has many useful features, including automated rollouts and rollbacks, horizontal scaling, automatic storage orchestration, and more.
3. RabbitMQ
RabbitMQ is a lightweight message broker, which is ideal for managing communication within a microservice application (see ‘dumb pipes’ above). It allows microservices to send and receive messages asynchronously. RabbitMQ supports several messaging protocols and can easily be deployed to both on-premise servers and cloud platforms.
4. Amazon SQS
Amazon SQS (Simple Queue Service) is a fully managed alternative to the open-source RabbitMQ tool. It was created with distributed systems and the microservices architecture in mind.
It automates the maintenance and scaling of messaging queues of microservice applications running on AWS, Amazon’s serverless platform (Google Cloud’s alternative solution is called Pub/Sub).
5. Raygun
Raygun lets you monitor your microservice application from a user-friendly, integrated dashboard. You can use it to track each microservice and deployment, set up alerts to catch issues in real time before they could affect your application, visualize your code during execution, and more.
It includes both a crash reporting and an application performance monitoring tool (accessible from the same dashboard) so that you’re covered for both real crashes and issues that might lead to crashes in the future.
What does the microservice architecture look like in 2023 and beyond?
Finally, let’s see some real-world examples of the microservices architecture created with Java frameworks, containerized with Docker, and deployed to different cloud platforms.
Microservices in Java
Java is one of the best languages to develop microservices. There are several Java frameworks that support the microservices architecture, including:
- Dropwizard
- JHipster
- Spark
- Spring
- Swagger
- Play
- Vert.x
Using Spring Boot is the most popular way to build microservices in Java. Spring Boot is a utility built on top of the Spring platform. It makes it possible to set up standalone Spring apps with minimal configuration. Spring Boot can save a lot of time by automatically configuring Spring and third-party libraries.
For instance, here’s a very simple microservices example by Paul Chapman, published on the official Spring blog. It uses Spring, Spring Boot, and Spring Cloud to build the application.
You can find the full code examples in the tutorial, but here we’ll only look into the application structure. The steps are as follows:
- Create the service registration (so that microservices can find each other), using the Eureka registration server (incorporated in Spring Cloud)
- Create an account management microservice called “Account Service” with Spring Boot
- Create a web service to access the microservice, using Spring’s
RestTemplate
class
Here’s an illustration of the app’s structure:
Note that this is a very simple example, and in a real-world application, the web service would make requests to more than one microservice.
For more examples of how to build microservices in Java, check out these two excellent tutorials: - Currency conversion microservice with Spring Boot by Ranga Karanam
- Reactive Java microservices with Spring Boot and JHipster by Matt Raible
Microservices in Docker
As mentioned above, Docker is a great tool for deploying microservice applications. You have different options to structure a microservice application as Docker containers. For example, you can: - deploy each microservice in its own Docker container - break down a microservice into various processes and run each from a separate container - use Docker Compose to run multi-container applications — this can save you a lot of time, as you don’t have to create and start each container separately.
With Docker Compose, you can configure your app’s microservices using a YAML file. Here’s a useful tutorial on how to deploy microservices with Docker and Docker Compose by Bob Strecansky.
If you have a large-scale application with several containers, you can also make use of a container orchestration platform. The two most popular tools are Docker Swarm and Kubernetes (see above). Docker Swarm is embedded in the Docker Engine; it’s Docker’s native orchestration tool. While Swarm fits well into the Docker ecosystem and is easy to set up, Kubernetes is more customizable and has higher fault tolerance.
Microservices in the cloud
Microservices are frequently run as cloud applications because they are lightweight and easy to deploy. Popular cloud platforms come with several microservice-friendly features, such as: - On-demand resources
- Infrastructure as code - Continuous deployment and delivery - Managed services - A large choice of programming languages, operating systems, database technologies, etc.
To learn more about what the microservice architecture looks like in the cloud, check out one or all of the following resources: - How to build microservices on Google Cloud Platform and App Engine by Pablo Portillo
- Guide to implementing microservices architecture on AWS by Anna Dziuba - Tutorial for building microservice applications with Azure Container Apps and Dapr by Taiseer Joudeh
Conclusion
The microservice architecture is most suitable for large-scale applications developed by distributed teams and running in the cloud, while smaller apps are often better off with a monolithic code base. Microservices have both their advantages and disadvantages: it’s easier to develop and maintain independent services, but managing communication between them and preparing for failure requires additional effort. Container platforms, DevOps practices, and cloud computing can help a lot in adopting the microservices architecture.
Maintaining and monitoring microservices is an important part of developing microservices. Raygun APM, Real User Monitoring, and Crash Reporting are designed with modern development practices in mind. See how the Raygun platform can help keep your microservice applications performant.