Table of contents
In recent years, there’s been quite a buzz around so-called microservices, also known as microservices architecture. With software applications becoming increasingly complex, they have also become bulkier and more difficult to manage, update, and maintain.
Microservices architecture is a relatively new software development approach that addresses this problem by splitting up complex software into independent building blocks or services. These modular services can be deployed and managed individually, making large applications more robust to change.
Microservices as an architectural style dates back to 2005 when Peter Rodgers, the originator of Resource Oriented Computing, first coined the term “Micro-Web-Services”. In an attempt to create a more flexible and service-oriented software architecture, he successfully outlined the principles of a well-designed microservices platform. Two years later, software architect Juval Löwy expanded on the idea of a granular use of services and inspired several expert-led case studies with promising outcomes.
Today, microservices architecture is not only considered a hot topic but the future of efficient software development and maintenance.
Microservices is an architecture style, in which large complex software applications are composed of one or more services. Microservices architecture describes a particular style in which software applications are designed. In contrast to traditional architectural styles that aim to build software like a single unit, microservices architecture is based on a modular approach. Thus, applications are developed as suites consisting of multiple microservices that can be independently deployed and managed. Each of these microservices focuses on completing one task only and does that one task really well. In all cases, that one task represents a small business capability.
But what are microservices exactly? Even though there is no clear definition for microservices per se, there are certain characteristics that are associated with this revolutionary software development approach.
One of the main characteristics is the way the architectural style componentizes a complex application. The software is broken down into multiple components that are built as independently replaceable and upgradeable services. These services can be defined as processes that often communicate over a network using protocols that are technology-agnostic.
Microservices are also built to suit business capabilities. And since they are decentralized and small in size, they offer a lot of flexibility when it comes to programming languages, hardware, and software environment. Figure 1 shows a sample application using microservices.
Figure 1: A sample application using microservices
Also, microservices can be developed in any programming language. They communicate with each other using language-neutral application programming interfaces (APIs) such as Representational State Transfer (REST). Microservices also have a bounded context. They don’t need to know anything about underlying implementation or architecture of other microservices.
In the Microservices world, developers have the freedom and capability to experiment with new languages, patterns, frameworks, data stores, and other innovative aspects of IT development. This can result in the operations team feeling a level of discomfort when confronted with new experiments done by the developers.
Various views exist in regard to Microservices architecture having no governance or thin governance compared to service-oriented architecture (SOA).
How do Microservices work?
The idea of loosely coupled services that are highly maintainable and testable sounds intriguing to companies that face IT challenges due to complex applications. But before uprooting the system in place, it is vital to understand how microservices actually work. In order to create a functioning modular architecture, a business needs to first identify its capabilities and define the services that will run independently from each other.
A microservice architecture diagram is needed to expose dependencies that would inhibit the flexibility of each service. It is critical to ensure that an error in an individual part wouldn’t impact the whole system.
But even though microservices are designed to work independently, they sometimes still have dependencies on other microservices and need to communicate with each other. This communication occurs through the use of APIs (Application Programming Interfaces); synchronous or asynchronous protocols like HTTP/REST or AMQP. Ideally, each microservice has its own database in order to decentralize responsibility and manage updates individually. When using the “Database per Service” pattern, a saga, which is a transaction that spans multiple services, is put in place in order to maintain data consistency.
However, it is not uncommon that each microservice develops more and more service dependencies over time. This is a normal occurrence once services begin to grow and start affecting the number of instances, locations, and protocols.
API gateways help avoid problems that arise from these changes. And this is how it works: An API gateway takes all API requests and then routes every single one to the most appropriate microservice. It also invokes backend services and aggregates the results to identify the best path. It serves as the entry point for every request, organizes all input, and creates a simplified user experience.
Characteristics of Microservices
- Each service is a light-weight, independent, and loosely-coupled business unit.
- Each service has its own codebase, managed and developed by a small team (mostly in an agile environment).
- Each service is responsible for a single part of the functionality (business capability), and does it well.
- Each service can pick the best technology stack for its use cases (no need to stick into one framework throughout the entire application).
- Each service has its own DevOp plan (test, release, deploy, scale, integrate, and maintain independently).
- Each service is deployed in a self-contained environment.
- Services communicate with each other by using well-defined APIs (smart endpoints) and simple protocols like REST over HTTP (dumb pipes).
- Each service is responsible for persisting its own data and keeping external state (Only if multiple services consume the same data, such situations are handled in a common data layer).
Benefits of Microservices
Microservice are made to scale large systems. They are great enablers for continuous integration and delivery too.
Figure 2:The Scale Cube: 3-Dimensional Model for Scalability (Image: Nginx Blog)
- Independent scaling: Microservices architecture supports Scale Cube concept described in the excellent book The Art of Scalability. When developing microservices to achieve functional decomposition, the application automatically scales via Y axis. When the consumption is high, microservices can scale via X axis by cloning with more CPU and memory. For distributing data across multiple machines, large databases can be separated (sharding) into smaller, faster, more easily managed parts enabling Z axis scaling.
- Independent releases and deployments: Bug fixes and feature releases are more manageable and less risky, with microservices. You can update a service without redeploying the entire application, and roll back or roll forward an update if something goes wrong.
- Independent development: Each service has its own codebase, which is developed, tested, and deployed by a small focused team. Developers can focus on one service and relatively-small scope only. This results in enhanced productivity, project velocity, continuous innovation, and quality at source.
- Graceful degradation: If a service goes down, its impact won’t propagate to the rest of application and result in a catastrophic failure of the system, allowing a certain degree of anti-fragility to manifest.
- Decentralized governance: Developers are free to pick the technology stacks and make design standards and implementation decisions that are best suited for their service. Teams do not have to get penalized due to past technology decisions.
Operational Concerns of Microservices
Independent services alone cannot form a system. For the true success of microservices architecture, significant investments are required to handle cross-system concerns like:
- Service replication: a mechanism by which services can easily scale based upon metadata
- Service registration and discovery: a mechanism to enables service lookup and finds the endpoint for each service
- Service monitoring and logging: a mechanism to aggregate logs from different microservices and provide a consistent reporting
- Resiliency: a mechanism for services to automatically take corrective actions during failures
- DevOps: a mechanism for handling continuous integration and deployment (CI and CD)
- API gateway: a mechanism for providing an entry point for clients
Middleware & Design Patterns
API Gateway Style Microservices Architecture, this is the most common design pattern used in microservices. API Gateway is an intermediary with minimal routing capabilities and just acting as a ‘dumb pipe’ with no business logic inside. In general, API Gateway allows you to consume a managed API over REST/HTTP.
Other types of microservices integration patterns: Point-to-point style (invoking services directly from client side app) and Message Broker style (implementing asynchronous messaging).
Figure 3: API Gateway Style Microservices Architecture
API Gateway acts as a single entry point for all clients as well as an edge service for exposing microservices to the outside world as managed APIs. It sounds like a reverse proxy, but also has additional responsibilities like simple load-balancing, authentication & authorization, failure handling, auditing, protocol translations, and routing. The development team can select one of the following approaches to implement an API Gateway.
Build it programmatically: to have better customizations and control
Deploy an existing API gateway product: to save initial development time and use advanced built-in features (Cons: Such products are vendor-dependent and not completely free. Configurations and maintenance often can be tedious and time-consuming)
Few design patterns that explain API Gateway behaviour are as follows
- Gateway Aggregation: aggregate multiple client requests (usually HTTP requests) targeting multiple internal microservices into a single client request, reducing chattiness and latency between consumers and services.
- Gateway Offloading: enable individual microservices to offload their shared service functionality to the API gateway level. Such cross-cutting functionalities include authentication, authorization, service discovery, fault tolerance mechanisms, QoS, load balancing, logging, analytics etc.
- Gateway Routing: (layer 7 routing, usually HTTP requests)— route requests to the endpoints of internal microservices using a single endpoint, so that consumers don’t need to manage many separate endpoints
Note that an API Gateway should always be a highly-available and performant component, since it is the entry point to the entire system. (Read Design patterns for microservices)
2.Event Bus: (Pub/sub Mediator channel for asynchronous event-driven communication)
Figure 4: Eventual Consistency between microservices based on event-driven async communication
For different parts of the application to communicate with each other irrespective of sequence of messages (asynchronous) or what language they use (language agnostic), event bus can be used. Most of event buses support publish/subscribe, distributed, point to point, and request-response messaging. Some event buses (like in Vert.x) allow client side to communicate with corresponding server nodes using the same event bus, which is a cool feature loved by full-stack teams.
3.Service Mesh: (Sidecar for interservice communication)
Figure 5: Interservice communication using Service Mesh style
Service Mesh implements Sidecar pattern by providing helper infrastructure for interservice communication. It includes features like resiliency (fault tolerance, load balancing), service discovery, routing, observability, security, access control, communication protocol support etc.
In practice, a Sidecar instance is deployed alongside each service (ideally in the same container). They can communicate through primitive network functions of the service. The Control Plane of Service Mesh is separately deployed to provide central capabilities like service discovery, access control, and observability (monitoring, distributed logging). Most importantly, Service Mesh style allows developers to decouple network communication functions from microservice code and keep services focused only on the business capabilities. (Read: Netflix Prana, Service Mesh for Microservices)
4.Backends for Frontends (BFF)
If the application needs to tailor each API to suit the client app type (web, mobile, different platforms), different rules (configs) can be enforced via a facade or can serve separate builds based on client capabilities. This can be implemented at the API Gateway level itself or in parallel to the services level. This pattern is useful for providing specific user experiences. However, the development team should be careful enough to keep BFFs upto a manageable limit.
Figure 6: Implementing Backends for Frontends and Aggregator patterns at API Gateway
What is a Microservices Governance?
Microservices Governance is a methodology or approach that establishes policies, standards, and best practices for the adoption of Microservices to enable an enterprise agile IT environment.
Governance with monoliths is centralized. Decisions are made top-down, rigid control is maintained to ensure standards are in place across the organization and within the application stack. Over time, this model degenerates, creating a stagnant technological and architectural system, thus minimizing innovation.
Microservices promote a polyglot model regarding technology stacks for supported languages, tools, and data stores. The main concept of these Microservices is the reusability of assets and tools which can be decentralized. The core theme of decentralization governance is the concept of building and running it.
This decentralized model is best suited for Microservices governances. The benefits of decentralized governance give Microservices teams the freedom to develop software components using different stacks.
Elements of Microservices Governance
Microservices Governance plays a critical role in the success of Microservices initiatives. The failure to implement proper governance mechanisms can result in an unmanageable and unstable architecture; however, with the correct governances in place, you can avoid the distributed mess of services while gaining the business's support.
A Benefit of Microservices Governance is the fact that it results in a significantly higher return on enterprise investments. However, for return on investments, it is crucial to establish policies along with clear communication channels where accountability and effectiveness can be measured.
A strong Microservices Governance foundation contains three elements: people, process, and technology. For a successful functioning Microservices governance, these three elements must align.
The following diagram demonstrates these elements:
Figure 7: Elements
The Microservices Governance Framework
Based on experience and adoption of Microservices, the Microservices governance framework emphasizes the following:
Business • Strategic alignment of IT with Business: Derive the IT mission to align with the vision of the business. • Reusability of services and quick-time-to-production.
- Microservices Reference Architecture: An architecture guideline for service design and integration with enterprise usage systems, etc.
- Identifying, specifying, creating, and then deploying Microservices through DevOps methodology.
- Enterprise-wide service policies, common infrastructural services.
- Developing fine-grained services by composition and orchestration.
- Service monitoring and management: Management and monitoring of access policies, Quality of Service (QoS) metrics, and KPIs to gauge throughput and performance.
- Establish a dedicated Microservices enterprise to govern effective management
- Enterprise structure which delegates who will authorize critical decisions in regard to the Microservices adoption and usage
- Establish standards on how decisions will be made, implemented, and monitored
Five things enterprise architects should know about microservices
Indeed, microservices can be built and deployed independently of one another, improving not only developer productivity and flexibility but also application resiliency. Microservices can be constructed from the ground up, or legacy monolithic applications can be broken down into microservices. In either case, microservices are a key enabler of development in a cloud-native model.
However, microservices have a reputation for complexity. Also, not every monolith is well suited to be transformed into a set of microservices. Enterprise Architects play a crucial role in helping the organization determine when and where microservices make sense and—perhaps more importantly—laying the groundwork for the cultural shift that needs to occur for microservices to be successful.
1. Microservices hold a lot of promise According to research by Forrester, an effective microservices implementation has the potential to increase the rate of business innovation and responsiveness by: • Speeding app delivery • Enabling quick, incremental app changes • Ensuring resiliency at scale • Optimizing production costs • Supporting graceful editing • Freeing developers to use the best tools for the job
Indeed, according to research by TechRepublic Premium, companies that have adopted microservices are realizing some of these benefits.
TechRepublic Premium conducted an online survey in the spring of 2020 to gauge awareness about microservices, as well as usage and benefits. Organizations that have integrated microservices noted that they see faster deployment of services (69%). Respondents also reported greater flexibility to respond to changing conditions and the ability to quickly scale up new features into large applications (61% and 56%, respectively).
2. Microservices can introduce or shift complexity
The microservices model makes sense on paper, but not always in practice. Microservices can easily get out of hand—especially if an organization dives into the microservices deep end from the get-go.
“A badly designed microservices approach can lead to ‘microservice sprawl,’” states the Capgemini report Monolith to Microservices, an Integration Journey. Without centralized control and discipline, the report continues, “the number of microservices will grow exponentially and create an IT landscape that is complex, difficult to manage, and actually reduce reuse rather than increase it.” To avoid this kind of sprawl, microservices planning must include tools and platforms for management, orchestration, and company-wide training.
3. Microservices requires a significant cultural shift
Perhaps the biggest predictor of an organization’s potential for reaping the rewards from microservices is its willingness and ability to make a pretty significant cultural shift.
In the traditional development model, each team involved in developing an application works in a siloed fashion, throwing its work “over the wall” after it’s finished. Microservices are built more cohesively, with cross-functional teams responsible for developing their microservices and the customers of their microservices. This enables organizations to focus development on a business problem and update services as soon as business needs and/or goals dictate. It’s all good, especially from an Enterprise Architect viewpoint. However, if the organization’s departments are siloed and teams are not working collaboratively, it’s not easy to get there.
4. Not every monolith (or everything in a monolith) is made for microservices
In their enthusiasm for microservices, some organizations go all in—picking away at every piece of their monoliths to pull out microservices. However, sometimes it doesn’t make sense to “microservice-ize” certain parts of a monolith—or even to touch the monolith at all. Indeed, when it comes to monoliths, the best course of action often is: If it isn’t “broken,” don’t fix it.
In a great piece on Red Hat Enable Sysadmin, Eric D. Schabell recommends questions to ask when thinking about moving monolithic applications to microservices: • What is the performance impact of microservices? • How do we handle state while splitting up our monoliths? • How do we handle our data with distributed microservices? • How do we test stateful micro(services)? • Should we use API management or a service mesh? In general, organizations should look for discrete functions that will work effectively when coupled with other microservices within an application. Organizations should also make sure they don’t make microservices too big (defeating the purpose of microservices in the first place) or too small (increasing the potential for that “microservice sprawl” mentioned earlier).
5. Microservices require vital enabling technologies and tools
One of the benefits of (and ways of getting people on board for) microservices is that teams can pick the tools and languages they use to develop microservices. However, according to IBM Cloud Learn Hub, several core tools are essential for microservices implementation:
Containers, Docker, and Kubernetes Containers, a model popularized by Docker, are the compute model most closely associated with microservices, states IBM Cloud Learn Hub. And, just like microservices, containers can quickly proliferate. Kubernetes has become the de-facto standard for managing and orchestrating containers.
API gateways Microservices can communicate directly with each other, but API gateways are often used as microservices-based applications grow. Organizations can use API management platforms, or if the microservices architecture is being implemented using containers and Kubernetes, a service mesh like Istio can be used, notes IBM Cloud Learn Hub.
Messaging IBM Cloud Learn Hub notes that it may be best practice to design stateless services, but that state nonetheless exists and services need to be aware of it. State-establishing API calls can be combined with messaging or event streaming to provide more flexibility. Not every service will process messages as quickly as they receive them, so flexibility is critical. This can be accomplished by a general-purpose message broker or an event streaming platform such as Apache Kafka.
Serverless The serverless model allows developers to build and run applications without having to manage servers. Servers are still part of the equation, of course, but they are abstracted away from the app development process. The unit of execution in serverless is a function, which is commonly understood to be even smaller than a microservice. Functions run in the public cloud can be spun and scaled up and down as needed, with organizations paying for what they need as they go. The goal of both functions and microservices is to create more efficient deployment units and scale and update on demand. Organizations can use both, depending on the workload, to increase overall efficiency and resiliency.
1.Domain Driven Design — Model services around the business domain.
Figure 8: application design
To handle large models and teams, Domain Driven Design (DDD) can be applied. It deals with large models by dividing them into different Bounded Contexts and being explicit about their interrelationships and underling domain. These bounded contexts can be converted into separate microservices at the application design level.
2.Decentralized Data Management (Avoid shared databases). When multiple services consume a shared data schema, it can create tight coupling at data layer. To avoid it, each service should have its own data access logic and separate data store. The development team is free to pick the data persistence method which best fit to each service and nature of data.
Figure 9: Avoid shared data stores and data access mechanisms
3.Smart endpoints and dumb pipes — Each service owns a well-defined API for external communication. Avoid leaking implementation details. For communication, always use simple protocols such as REST over HTTP.
4. Asynchronous communication — When asynchronous communication is used across services, the data flow does not get blocked for other services.
Figure 10: Synchronous vs. asynchronous messaging
5. Avoid coupling between services — Services should have loose coupling and high functional cohesion. The main causes of coupling include shared database schemas and rigid communication protocols.
6. Decentralize development — Avoid sharing codebases, data schemas, or development team members among multiple services/projects. Let developers focus on innovation and quality at source.
7.Keep domain knowledge out of the gateway. Let the gateway handle routing and cross-cutting concerns (authentication, SSL termination).
8.Token-based Authentication — Instead of implementing security components at each microservices level which is talking to a centralized/shared user repository and retrieve the authentication information, consider implementing authentication at API Gateway level with widely-used API security standards such as OAuth2 and OpenID Connect. After obtaining an auth token from the auth provider, it can be used to communicate with other microservices.
9.Event-driven nature — Human beings are autonomous agents that can react to events. Can’t our systems be like that? (Read: Why Microservices Should Be Event Driven: Autonomy vs Authority)
10.Eventual consistency — Due to the high cohesiveness in microservices, it is hard to achieve strong consistency throughout the system. Development team will have to handle eventual consistency as it comes.
11.Fault tolerance — Since the system comprises of multiple services and middleware components, failures can take place somewhere very easily. Implementing patterns like circuit-breaking, bulkhead, retries, timeouts, fail fast, failover caching, rate limiters, load shedders in such vulnerable components can minimize the risks of major failures. (Read: Designing a Microservices Architecture for Failure)
12.Product engineering — Microservices will work well as long as it is engineered as a product, not as a project. It’s not about making it work somehow and delivering before the deadlines, but about a long term commitment of engineering excellence.
The bottom line
The value of microservices is clear, but there are lots of blurry lines along the way. With their 360-degree view on the organization, its legacy technology, and the culture that exists among and between different departments, Enterprise Architects are uniquely suited to offer the clarity needed to adopt and apply microservices technology strategically.