Microservice architecture is one of the most discussed software architecture trends at the moment, and it has forever changed the way enterprise applications are built. Rather than the slow, complex monolithic approach of the past, developers and companies everywhere are turning to a microservices architecture to simplify and scale their structures.
In fact, even companies like Amazon, Netflix, Spotify, and Uber have made the transition.
Whether you want to get started with Microservices or you’re just curious about the debate surrounding it, you’re in the right place. Today, I will walk you through everything you need to know about Microservices, from real-world examples to architecture patterns and more.
Today, we will cover the following:
- What is Microservices Architecture?
- Benefits and drawbacks
- Microservices and Docker
- Tech stacks and architecture patterns
What is Microservices Architecture?
There is no universal definition of the term microservices. The simplest definition of microservices, also called microservice architecture, is an architectural style that structures an application using loosely coupled services. These collections or modules can be independently developed, deployed, and maintained.
They operate at a much faster and more reliable speed than the traditionally complex, monolithic applications. Using microservice architectures, an organization of any size can evolve technology stacks tailored to its capabilities.
There are many tangible benefits to using microservices, which we will discuss later, but there is still some controversy over whether or not companies should switch from a monolithic to microservices architecture. Let’s examine the difference between the two to understand the debate.
Monolithic vs. Microservices
The monolithic architecture is the traditional way of building and deploying applications. This structure is based on the concept of a single, indivisible unit, including the server side, client side, and database. All facets are unified and managed as a single unit and codebase. This means that any updates must be made to the same codebase, so the whole stack must be altered. As monolithic applications scale, they can become quite complex, so the overall development is generally longer.
A microservices architecture, on the other hand, breaks down that unit into independent ones that function as separate services. This means that every service has its own logic and codebase. They communicate with each other through APIs (Application Programming Interfaces).
So, which architecture should you choose? Let’s break it down.
Choosing a monolithic architecture
- If your company is a small team. This way, you don’t have to deal with the complexity of deploying a microservice architecture.
- If you want a quicker launch. Monolithic architecture requires less time to launch. This system will require more time later on to update your system, but the initial launch is quicker.
Choosing a microservices architecture
- If you want to develop a more scalable application. Scaling a microservices architecture is far easier. New capabilities and modules can be added with much ease and speed.
- If your company is larger or plans to grow. Using microservices is great for a company that plans to grow, as a microservices architecture is far more scalable and easier to customize over time.
Benefits and drawbacks of microservices
There are a number of reasons why a microservices architecture is a better choice for your company. Let’s discuss the most notable benefits and then examine some of the drawbacks.
Improves Scalability and Productivity
Large teams often have to work together on complex projects. With microservices, projects can be divided into smaller, independent units. This means that teams can act independently regarding domain logic, which minimizes the coordination and effort. On top of that, the teams responsible for each microservice can make their own technology decisions depending on their needs.
For example, the internal structure of each unit or container does not matter as long as the interface functions correctly. Therefore, any programming language can be used to write a microservice, so the responsible team can select the best language for their teammates.
Integrates well with legacy systems
Monolithic systems are hard to maintain. Many legacy systems are poorly structured, poorly tested, or depend upon outdated technologies. Luckily, microservices can work alongside legacy systems to improve the code and replace old parts of the system. Integration is easy and can solve many of the problems that make monolithic systems something of the past.
Microservice architectures create systems that remain maintainable in the long run since the various parts are replaceable. This means that a microservice can easily be rewritten without compromising the whole system. As long as the dependencies between microservices are managed appropriately, changes can easily be made to optimize team needs and performance.
Microservices are best for distributed teams. If you have teams around the world or various divisions, microservices grant the necessary freedoms and flexibility to work autonomously. Technical decisions can be made quickly that integrate with other services in a flash. Cross-functionality has never been easier.
Deployment requires more effort
The operation of a microservice system often requires more effort since there are more deployable units that must each be deployed and monitored. Changes to interfaces must be implemented so that an independent deployment of individual microservices is still possible.
Testing must be independent
Since all microservices must be tested together, one microservice can block the test stage and prevent the deployment of the other microservices. There are more interfaces to test, and testing has to be independent for both sides of the interface.
Difficult to change multiple microservices
Changes that affect multiple microservices can be more difficult to implement. In a microservice system, changes require several coordinated deployments.
Microservices and Docker
Docker and Microservices are nearly synonymous. Microservices must be separately deployable, scalable independent units. But what if you create multiple microservices for your application? Docker is a lightweight solution for deploying microservices. A microservice can be packed into a Docker image and isolated as a Docker container. This way, you can build an application that is independent of your host environment.
Instead of having a complete virtual machine of their own, Docker containers share the kernel of the operating system on the Docker host. The processes from the containers appear in the process table of the operating system on which the Docker containers are running.
To use Docker with microservices, you need to create Docker images via files named Dockerfile. Dockerfiles are easy to write, so rolling out software can be easy. Take a look at an example of a Dockerfile for a Java microservice.
COPY target/customer.jar .
CMD /usr/bin/java -Xmx400m -Xms400m -jar customer.jar
A typical microservice system contains multiple Docker containers. Coordinating a system of multiple Docker containers requires configurations for the virtual network. Containers must be able to find each other in order to communicate. The Docker Compose environment can contact another server via a link, offering a service discovery system.
Technology stacks and architecture patterns
It is one thing to understand how microarchitecture works and another to actually build and implement it. That’s why we want to focus on the various technologies available to you for an entire microservices system. Let’s walk through some different tech stacks, patterns, and designs for creating an executable microservice architecture.
Micro and Macro architecture decisions
It is advisable to divide your architecture into micro and macro architecture. The micro architecture involves all decisions made for each microservice. The macro architecture involves all decisions made at a global level that apply to all microservices.
It is possible to extend the concept of micro and macro architecture to technical decisions. Technical decisions can be made within the framework of macro or microarchitecture. For example, take a look at the technical decisions to be made at the micro and macro levels for a database:
- Micro: Each microservice can have its own instance of the database. If databases were defined at the microarchitecture, a crash of one database will only lead to the crash of one microservice. This makes the whole app far more robust.
- Macro: The database can also be defined as part of the macro architecture. Multiple microservices must not share a database schema.
A self-contained system (SCS) is a type of microservice architecture that specifies the elements of a macro architecture. This means they do not represent the whole system. Since an SCS is self-contained, it provides everything you need to implement one part of the domain logic, such as log data and a UI. SCSs also have an optional API.
For example, an SCS for a microservice payment would store information relevant to that payment as a bounded context. It would also implement the UI to show the payment history, and data about the customers would be replicated from other SCSs.
Think of these as a collection of best practices; SCSs provide precise rules based on established patterns, offering a point of reference for how to build a microservice architecture. All of these rules ensure that an SCS implements a domain, so an added feature only changes one SCS.
We can think of an SCS as a microservice architecture because it can be deployed independently and divide a system into independent web applications. In fact, one SCS can even be split into several microservices. They differ from microservices in three main ways: they are larger than microservices, they focus on loose coupling, and they must have a UI.
Microservices can also be integrated with a web front-end. Dividing the front end into different modules helps to solve some of the problems that come from treating it as a monolith. A modularized front-end is made up of separately deployable microservices. This can bring many benefits to your front-end.
For example, a modularized frontend can have independent domain logic, and a change in the domain can be implemented simply by modifying only one microservice. To combine separate front-ends, they must be integrated, so an integration system is necessary.
Front-end Monolith divided into Modules
This can be accomplished through links, where one frontend displays a link that another frontend reads and handles. This can also be accomplished through redirects, for example, how OAuth2 handles frontend integration. Redirects combine data transfer with frontend integration.
There are, however, a few exceptions when a frontend should be deployed as a monolith. For example, native mobile applications should be deployment monoliths, or if there were a singular team responsible for frontend development.
Synchronous microservices make a request to other microservices while it processes requests and waits for results. Asynchronous communication protocols send messages that the recipients react to, but there is no direct response. A microservice could be defined as asynchronous if it does not make requests to other microservices while processing, or it makes requests but does not wait for results.
Communication without waiting for a reponse (fire-and-forget)
Asynchronous microservices offer several notable advantages to synchronous microservices and resolve many of the challenges of distributed systems. The logic required to handle microservice requests does not depend on results, making them far more independent.
Similarly, if a communication partner were to fail, it does not crash the whole system, offering overall resilience to your system. On top of that, the processing and delivery are almost always guaranteed.
Some common examples of technologies for asynchronous microservices are Kafka (a MOM commonly used for messaging), REST, and Atom data format (for additional infrastructure).
Microservice platforms, such as PaaS and Docker scheduler, support the operation and communication of your microservices. These technologies enable communication between microservices for deployment, log analysis, and monitoring.
For example, these platforms support HTTP and REST with load balancing and service discovery. Limited operations support is needed for the implementation of microservices, so they can be quickly deployed and support multiple microservices.
Features of Microservices platforms
Microservices platforms represent a simplification and solution to common issues. Some notable platforms are Kubernetes and Docker, which is very important for microservices operations. PaaS and Cloud Foundry are also useful, but they are not as popular.
It’s important to note that migration to these platforms requires a change to the operation and installation of applications, which can make the use of a microservices platform a big and timely step. This is the main drawback of microservice platforms.