Microservices
The content here is under the Attribution 4.0 International (CC BY 4.0) license
Microservices architecture is a design approach where a single application is composed of multiple loosely coupled, independently deployable services. Each service focuses on a specific business function and communicates with other services through APIs. This architecture promotes scalability flexibility, and faster development cycles by allowing teams to work on different services simultaneously. Microservices also enhance fault isolation, making it easier to identify and resolve issues. However, they introduce complexities in terms of deployment, monitoring, and inter-service communication.
AWS microservices definition
In a whitepaper by (Services, 2022) they defined three approaches to microservices:
- API Driven
- Event Driven
- Data streaming
In additino to that, it was also mentioned that, in distributed systems, events might be delivered multiple times due to retries, so it’s important to design your applications to handle this. Requests that were once thought to be a once time and done, now becomes a point of attention for designing microservices.
Influential figures in the microservices movement include (Fowler & Lewis, 2014), and (Newman, 2015), who have written extensively on the topic. Chris Richardson is also a notable figure, known for his work on microservices patterns and practices, he hosts a portal dedicated to the subject and has a book on the subject as well (Richardson, 2018).
Despite the popularity of microservices, there are still many misconceptions about them. For example, some people believe that microservices are always the best solution for every problem, while others think that they are just a buzzword without any real benefits. In reality, microservices can be a powerful tool for building scalable and maintainable applications, but they are not a silver bullet. It is important to understand the trade-offs involved and to carefully consider whether microservices are the right fit for a given project. In that sense, this space is dedicated to microservices, and it will contain resources, articles, and other content related to the topic.
Join me in exploring the world of microservices, where we will delve into the intricacies of designing, building, and maintaining microservices architectures. From understanding the fundamental principles to exploring advanced patterns and practices, this space will serve as a comprehensive resource for anyone interested in microservices.
Introduction
The history of microservices can be traced back to the early 2000s, when the concept of service-oriented architecture (SOA) was gaining popularity. SOA was a way of designing software systems as a collection of loosely coupled services that could communicate with each other over a network. SOA was getting traction in the industry for java based applications, and it was a way to build distributed systems that could scale horizontally. However, SOA often led to complex and heavyweight architectures, which made it difficult to develop and maintain applications. The pain points of SOA where caracterized by:
- Availability
- Security
- Scalability
- Performance
In the early 2010s, a new approach to software architecture emerged, which was later coined as microservices. Microservices are a way of designing software systems as a collection of small, independent services that can be developed, deployed, and scaled independently. The following image illustrates the difference between a monolithic architecture and a microservices architecture (Fischer, 2022):
This approach allows for flexibility and agility in software development, as well as improved fault tolerance and scalability. However, microservices also introduce new challenges, such as increased complexity in managing distributed systems and the need for effective communication between services. (Felisberto, 2024) relates the widespread acceptance and popularization of distributed architectures (like microservices) by the advent of cloud computing.
The match of technologies
Microserivices are often associated with technologies such as RESTful APIs, message queues, and containerization. These technologies enable microservices to communicate with each other and to be deployed in a scalable and efficient manner. The rise of cloud computing has also played a significant role in the adoption of microservices, as it provides a flexible and cost-effective infrastructure for deploying and managing microservices.
Docker showed a rising interest around 2013 based on Google Trends data. Both, docker and Kubernetes are two of the most popular technologies for deploying and managing microservices.
Amazon, which had implemented SOA services, decomposed its services internally before the term microservices became common. Microservices focus on smaller, narrowly scoped services that can be developed, deployed, and scaled independently, facilitated by advances in virtualization, containerization (e.g., Docker), and cloud platforms.
Amazon and monoliths
Even though Amazon was an early adopter of microservices, recently Prime Video showed case that for streaming a monolith might be a fit to reduce costs by 90% and simplify the architecture.
The Istio project’s recent move back to a monolith, driven by the understanding that microservices aren’t ideal, reinforces the importance of strategic decision-making (Mendonça et al., 2021). Microservices architecture is not a one-size-fits-all solution, and it is important to carefully consider the trade-offs involved before adopting it. While microservices can provide significant benefits in terms of scalability, flexibility, and maintainability, they also introduce new challenges that need to be addressed.
Strategic monoliths
Strategic monoliths and microservices were the subject of a book by Vaughn Vernon, which explores the idea of using a monolith as a strategic approach to software architecture. It discusses the evolution and trade-offs between monolithic and microservices architectures, emphasizing that the choice should be driven by business needs rather than hype. I shared my thoughts on the book in the Strategic Monoliths and Microservices review.
Based on the natural complexity of the adoption or not of microservices, this space was created. It is dedicated to microservices, and it will contain resources and articles related to the topic.
Decomposition and design
Microservices architecture is characterized by the decomposition of applications into smaller, independent services that can be developed, deployed, and scaled independently. This approach allows for flexibility and agility in software development, as well as improved fault tolerance and scalability. The key characteristics of microservices include:
- Independence: Each service can be developed, deployed, and scaled independently, allowing for flexibility and agility in software development.
- Loose coupling: Services are loosely coupled, meaning that they can communicate with each other without being tightly integrated. This allows for flexibility in changing and evolving services over time.
- Single responsibility: Each service is focused on a specific business function, allowing for clarity and maintainability of the codebase.
- Scalability: Services can be scaled independently, allowing for flexibility in managing resources and handling traffic spikes.
- Resilience: Microservices architecture promotes fault tolerance and resilience, as failures in one service do not necessarily affect the entire system.
Communication patterns
The communication between microservices can be synchronous or asynchronous, depending on the requirements of the application. Synchronous communication is typically used for real-time interactions, while asynchronous communication is used for event-driven architectures and decoupling services. Both approaches have their advantages and disadvantages, and the choice of communication pattern depends on the specific use case. Let’s explore the two main communication patterns in microservices.
Synchronous
Synchronous communication in microservices is typically achieved through RESTful APIs or gRPC (but not limited to) (Services, 2022). In this pattern, services communicate with each other in real-time, sending requests and receiving responses.
This approach is suitable for scenarios where immediate feedback is required and blocking and waiting for the response is fine. However, it can lead to tight coupling between services and increased latency due to network overhead.
HTTP - REST
REST (Representational State Transfer) is a widely used architectural style for designing networked applications. It is based on a stateless, client-server communication model, where services expose resources through HTTP endpoints. RESTful APIs are widely supported across programming languages and frameworks. It is not uncommon to see concepts from fraameworks like SpringBoot to offer support out of the box for RESTful APIs.
Thoughts on REST APIs
Previously in this space, I wrote about my thoughts on REST APIs, which you can find a detailed discussion on the topic in the Thoughts on REST APIs page.
The initial definitions of REST were made by Roy Fielding in his doctoral dissertation in 2000 (Fielding, 2000). Later, Richardson defined the maturity model of RESTful services, which describes the different levels of maturity in RESTful API design. The model is divided into four levels, ranging from Level 0 (the Swamp of POX) to Level 3 (HATEOAS), with each level building upon the previous one (Fowler, 2010). RESTful APIs use standard HTTP methods (GET, POST, PUT, DELETE) to perform operations on resources, and they typically return data in formats like JSON or XML.
HTTP - GraphQL
GraphQL is a query language for APIs that allows clients to request specific data from a server. It provides a more flexible and efficient way to interact with APIs compared to traditional RESTful APIs. GraphQL allows clients to specify exactly what data they need, reducing over-fetching and under-fetching of data. It also supports real-time updates through subscriptions, making it suitable for applications that require real-time data synchronization.
GraphQL is often used in conjunction with microservices to provide a unified API layer that aggregates data from multiple services. It allows clients to query multiple services in a single request, reducing the number of round trips required to fetch data. GraphQL also provides strong typing and introspection capabilities, making it easier to understand and work with APIs.
A note about GraphQL and my experience with it
GraphQL is a powerful tool for building APIs, but it also comes with its own set of challenges. One of the main challenges is the complexity of managing the schema and ensuring that it remains consistent across different services. Additionally, GraphQL can lead to performance issues if not implemented correctly, as it allows clients to request large amounts of data in a single query.
I have seens GraphQL merging different schemas creating an invisible mesh that combined three different GraphQL apis, this approch helped the consumers to use a unified query for their needs, however, for maintainers of the GraphQL it was a point of attention, as it was not clear which mutations/queries belong to which api. I wrote about my experiences with GraphQL in the GraphQL Playground page.
Asynchronous (message queues, event-driven)
Asynchronous communication in microservices is typically achieved through message queues or event-driven architectures. In this pattern, services communicate with each other by sending messages or events, which are processed independently of the sender. This approach is suitable for scenarios where decoupling services is important. It is implemented using message brokers like RabbitMQ, Apache Kafka, or AWS SQS. These message brokers provide a way to decouple services and enable them to communicate without being tightly integrated. This allows for scalability and flexibility in handling requests, as well as improved fault tolerance and resilience.
Data management
Following the principles of microservices, each service should manage its own data and not share a database with other services. This approach promotes loose coupling and allows for independent scaling and deployment of services. However, it also introduces challenges in managing distributed data and ensuring consistency across services.
Saga
The Saga pattern is a way to manage distributed transactions in microservices. It involves breaking down a transaction into smaller, independent steps, each handled by a separate service. Each step is executed as a local transaction, and if any step fails, the Saga pattern provides a way to compensate for the failure by executing compensating transactions in reverse order.
Saga pattern
The slides by Caitie McCaffrey on the Saga pattern provide a comprehensive overview of the pattern and its implementation. Seaching the term in speackerdeck shows that the subject is popular.
Event sourcing
A widely used method for handling changes in microservices is event sourcing. In this pattern, each change to the application is captured as an event, building a chronological record of the system’s state. This not only facilitates debugging and auditing but also enables various components of the application to respond to the same events.
Event sourcing and CQRS
The Event Sourcing pattern is often used in conjunction with CQRS (Command Query Responsibility Segregation). CQRS is a pattern that separates the read and write operations of an application, allowing for different models for reading and writing data. This separation can improve performance and scalability, as well as simplify the design of the application. In a CQRS architecture, commands (writes) are handled by one service, while queries (reads) are handled by another service. This allows for scalability and performance, as well as the ability to replay events to rebuild the state of the application. Nat Pryce published his experiences with event sourcing and the errors made along the way.
Resources
Blog posts
- Microservices data design patterns
- Monolith vs Microservices: Which should you use
- Microservices Fundamentals
- PRODUCTION-READY MICROSERVICES - BOOK REVIEW AND MY PERSONAL NOTES
- STRATEGIC MONOLITHS AND MICROSERVICES - DRIVING INNOVATION USING PURPOSEFUL ARCHITECTURE - REVIEW
- What Are Microservices Design Patterns?
- Microservices Patterns: The Saga Pattern
- The Strangler Fig application pattern: incremental modernization to microservices
- The Struggle for Microservice Integration Testing
- Year-in-Review: 2023 Was a Turning Point for Microservices
-
Microservices vs Monolithic
- https://twitter.com/giovannibassi/status/1654272021642072066
- https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90
- How modular can your monolith go
Papers
- Testing in microservice systems: a repository mining study on open-source systems using contract testing
- Assessing the Impact of Migration from SOA to Microservices Architecture - paywall
Books
Youtube talks/videos
-
Micro Frontends for Java Microservices by Matt Raible
- 2014 the facto article for microservices
- do not start with microservices
- spring is a framework for microservices
- spring 2 - 2015
- spring web flux for busy systems
- it has a more complex syntax
- spring mvc for business as usual apps
- jhipster is an application generator
- made with npm
- it guides you creating a microservice or monolith application
- consul and eureka
- it defaults to consul
- java developers does not care about ui
-
Micro Frontends for Java Microservices
- this is the link in text format of the youtube video, the content is the same
- Testing Microservices: Join the Revolution By Victor Rentea
- This is a special YouTube playlist dedicated to microservices
References
- Services, A. W. (2022). Microservices on AWS. Amazon Web Services. https://docs.aws.amazon.com/pdfs/whitepapers/latest/microservices-on-aws/microservices-on-aws.pdf
- Fowler, M., & Lewis, J. (2014). Microservices. https://martinfowler.com/articles/microservices.html
- Newman, S. (2015). Building Microservices: Designing Fine-Grained Systems. O’Reilly Media.
- Richardson, C. (2018). Microservices Patterns: With examples in Java. Manning Publications. https://microservices.io/book
- Fischer, H. (2022). Testing in microservice systems: a repository mining study on open-source systems using contract testing.
- Felisberto, M. (2024). The trade-offs between Monolithic vs. Distributed Architectures. ArXiv Preprint ArXiv:2405.03619.
- Mendonça, N. C., Box, C., Manolache, C., & Ryan, L. (2021). The Monolith Strikes Back: Why Istio Migrated From Microservices to a Monolithic Architecture. IEEE Software, 38(5), 17–22. https://doi.org/10.1109/MS.2021.3080335
- Fielding, R. T. (2000). Architectural Styles and the Design of Network-based Software Architectures. https://www.bibsonomy.org/bibtex/217b085721104f50d2f804bd1df197edc/gromgull
- Fowler, M. (2010). Richardson Maturity Model. https://martinfowler.com/articles/richardsonMaturityModel.html