Microservice design principles
In the quest of building microservices, you will have to make several choices that will give a different flavor to your microservice architecture and its evolution over time. Microservice design principles provide the guidelines for evaluating key decisions that can affect the design of your microservice-based architecture. In principle, microservices support loose coupling and the modularity of services. Other principles can govern the design of a microservice architecture, but their importance can vary. We will cover those principles in the following sections.
Single responsibility principle and domain-driven design
A microservice should be responsible for offering a single feature or a group of related features to deliver a business capability. The only reason why a microservice interface should change is due to changes in the business capabilities offered by the microservice. This helps systems to be designed following the real-world domains and helps us visualize systems and architectures as a translation of real-world problems. Domain-driven design is an approach that helps with domain modeling and defining microservice boundaries, which helps us achieve modularity and reduces coupling.
Encapsulation and interface segregation
Each microservice owns its data, and the only way a service can communicate with other services is through well-defined interfaces. These interfaces are carefully designed by keeping their clients in mind. Rather than overloading microservice endpoints in client applications, a popular alternative is to introduce an API gateway, which will be explained later in the chapter. This technique is useful in delegating the communication responsibility to API gateways and keeping microservices focused on delivering business capabilities.
Culture of autonomy, ownership, and shared governance
The microservice architecture allows business capabilities owned by different teams to be delivered. These teams can work independently without requiring much collaboration across teams. Each team doesn't need to be assigned a single business capability; instead, they may choose from a related set of capabilities that belong to a single domain. The microservice architecture flourishes when you allow teams to have autonomy, since they can choose what they think is right to deliver the desired business capability. This doesn't mean that teams can do anything, though, but it certainly gives them the freedom to make decisions under an umbrella of formally agreed principles. These principles are called shared governance, which provides consensus across the teams regarding how they want to address different cross-cutting concerns, or how far they want to go to try different technologies. The team that builds the microservice owns it and is responsible for operationalizing it. Such cross-cutting concerns will be covered in detail in Chapter 7, Cross-Cutting Concerns.
Independently deployable
A single microservice should be independently deployable, allowing teams to roll out changes without them affecting other microservices. As part of shared governance, teams should continue to look for technologies and practices that can help them achieve better independence. This characteristic is extremely useful for operationalizing microservices at a large scale.
Culture of automation
Automation is an important concept that promotes the idea of finding opportunities for replacing manual steps with scripted programs to achieve consistency and reduce overhead. Continuous integration, continuous deployment, automated testing, and infrastructure automation are all different forms of automation that can help you reduce the overall overhead of managing microservices.
Designing for failures
Microservices are designed to be highly available and scalable. These services do fail but they should be designed to recover fast. The idea is that the failure of one microservice should not affect other microservices, which helps avoid cascading failures and allows the service to recover as fast as possible to restore the required functionality. Designing services for failure requires having a good understanding of user behavior and expectations. The goal is to keep the system responsive in case of service unavailability, alongside reduced functionality. However, service failures are inevitable and require a pragmatic approach that enables experimentation to find system vulnerabilities. This can be achieved with controlled experimentation that creates chaos to determine how the system would behave differently in different circumstances. This approach is classified as chaos engineering.
With chaos engineering, you experiment with specific areas of the system to identify their weaknesses. Chaos engineering is a practice where you intentionally introduce failures to find undiscovered system issues. This way, you can fix them before they occur unexpectedly and affect your business and users. This exercise also helps in understanding the risks and impacts of turbulent conditions, incident response, gaps in observability, as well as the team's ability to respond to incidents. Many tools can be used to perform chaos engineering, such as litmus, Gremlin, and a few others.
Observability
Observability is a capability that you build as part of a microservice architecture to allow you to identify and reason about the internal state of your system. It also helps with monitoring, debugging, diagnosing, and troubleshooting microservices and their core components in a production environment. In a microservice architecture, you collect logs, metrics, and traces to help teams analyze service behavior. Another important aspect of observability is distributed tracing, which helps in understanding the flow of events across different microservices. In practice, metrics are more important than other forms of monitoring. Many organizations invest a sizable number of resources in metrics, ensuring that they are available in real time, and then use them as the main tool for troubleshooting production issues.
In the next section, we will uncover some core fundamentals of microservices to build teams that are independent and autonomous for bringing agility.