Microservices Architecture – What is a Microservices Architecture?
The Open Group defines an architecture (in the TOGAF® 9.1 standard) as: “the structure of components, their inter-relationships, and the principles and guidelines governing their design and evolution over time”. It defines architectural style as: “the combination of distinctive features in which architecture is performed or expressed”.
Leveraging the above definitions, this White Paper examines and defines MSA in terms of:
- The problem space
- The combination of distinctive features
- Components, their interaction, and governance
MSA is a style of architecture that defines and creates systems through the use of small independent and self-contained services that align closely with business activities.
An individual microservice is a service that is implemented with a single purpose, that is self-contained, and that is independent of other instances and services. Microservices are the primary architectural building blocks of an MSA.
Conceptual Representation of a Monolith Application
Conceptual Representation of a Microservices Solution
The need for MSA is driven by the pain points below.
Decreasing the complexity of the development, operation, and management of services
Changes to components in solutions of interdependent applications and services has the challenge of even a small fix requiring a long change cycle as the entire solution must be validated or, in the case of a monolithic application, rebuilt. Even SOA applications may have dependencies and interactions required by services choreography.
The modularity of the components of a non-MSA application tends to weaken over time, making it harder and harder to make changes to only one small part of an application. The independent service nature required for an MSA allows for much easier development and deployment, since the services are both independent and self-contained.
Scaling of a monolithic or tightly-coupled application requires scaling of the entire application solution, rather than just the portion that is demanding more resources. Non-MSA SOA applications may have dependencies or sophisticated choreography requiring additional changes. Scaling of an MSA is achieved much more easily because instantiation of additional services is performed independently.
Simplify to decrease functional bloat
Styles other than MSA often expand the scope of the component parts by adding new functionality to a component or its interdependencies. This complicates the solution or application leading to bloat and demand on the system. With MSA, keeping each service independent and self-contained to a single atomic business function reduces this risk of bloating.
Allow faster response to changes in the marketplace or entry into a new market
An MSA is synchronized to business unit change velocity needs.
- There is joint ownership of the microservices by the business unit and the development team.
- As a result of joint ownership and the principles of single responsibility, independence, and self-containment, the microservices can be changed to cater to the changing needs of the business unit, irrespective of other business units of the enterprise.
MSA development and deployment does not require a large pre-existing infrastructure.
- Since a microservice is based on single atomic business functions they tend to be smaller.
- Therefore, due to the MSA characteristics, microservices can be developed in small environments. A large integrated solution or large monolithic application requires large investment in a development environment to support the management of even a small change.
The time and cost associated with development, operations, and management of large enterprise applications are reduced.
- Testing of a large enterprise application is a significant investment in time and resources, even for relatively minor updates to the application.
- Monolithic applications with centralized governance and data management tend to become dependent on a single technology addressing all business functions.
- Coordination and management of large developer teams requires a large amount of effort to be devoted to these overhead functions.
Advent of paradigms such as Continuous Integration (CI) and Continuous Deployment (CD), and the inability of the monoliths to meet these, as exemplified by the DevOps culture
The DevOps culture emphasizes the collaborative nature of application development and maintenance. It focuses on the elimination of silos between development and operations; i.e., the use of autonomous teams which own the entire life cycle of an application. The distributed, independent nature of microservices lends itself naturally to implementation in this kind of culture.
Continuous Integration (CI) and Continuous Deployment (CD) rely on near-constant integration of software, with automated deployment of the integrated code. The inherent modularity and independence of an MSA and its services lends itself very well to the CI and CD paradigms carried out by a number of distributed teams working off a controlled code base.
The advent of cloud and the need to distribute workload elastically between on-premise and cloud
MSA enables capabilities such as Web-Oriented Architecture (WOA) as well as allowing for leveraging the resilience and scalability offered by cloud.
Service independence allows for services to run anywhere and so cloud becomes a natural place to expand through adding more instantiations in support of demand or through expanding functions and services to an application through the addition of new independent services, which can reside in the cloud.
Highly responsive user experience
The resilience provided through the parallelism of an MSA, which is enabled by the independence of the microservices, permits rapid failover and self-recovery. These characteristics result in high availability and seamless user interaction.
An MSA will generally contain the following features:
- Software components are broken down into independent services (this is the fundamental feature of an MSA).
- Services are independently deployable.
- Services are mapped to atomic business capabilities.
- Services are fully decoupled.
- Scalability and resilience of the application are achieved through the independence of services and multiple parallel instances of services.
The key characteristic of MSA is that software components are implemented as services, rather than, for example, as libraries. These services are independently replaceable and deployable. Services are typically implemented using mandatory well-defined, published interfaces. An MSA will aim for defined service boundaries to avoid interface changes which will affect multiple services, but in some cases this may be unavoidable.
The applications built using MSA should keep the microservices decoupled and fully independent. Any choreography in an MSA is performed by the initiating application and not from within or by the microservices. This is done by leveraging simple protocols (such as REST) rather than complex products or a central tool.
Governance in an MSA is decentralized or distributed. This allows the developer teams to focus on determining the best technology platform or language implementation to solve their particular problem, and not be trapped in a “one size fits all” paradigm.
Determining the right granularity of services in an MSA is more of an art than a science.
However, it is imperative for organizations to set the rules of thumb up-front in determining the right level of granularity. Taking a too coarse-grained approach to services can result in a “monolith”, while taking a too fine-grained approach can result in an anti-pattern, referred to as “nano-services” (see APPENDIX A: Service Granularity).
A granularity line can be defined based on business activity, business agility, and other considerations.
Services that fall around the granularity line are considered microservices; those way above are the monoliths and those way below are the nano-services.
Right Level of Granularity
An MSA builds on many well established architectural, design, development, and operations paradigms such as object-oriented programming, SOA (see the chapter on SOA and MSA), and Domain-Driven Design (DDD), which provides a set of principles and methods to manage complexity by identifying core and ancillary domains and addressing architecture, development, operations, teams, etc. within the bounded context of each domain.
Another very relevant model is the scale cube presented in the referenced book “The Art of Scalability” (refer to http://theartofscalability.com). The scale cube defines a model for scalability through decomposition. Of particular interest in the scale cube is the y-axis which focuses on functional decomposition or services.
The definition of MSA, earlier in this White Paper, articulates that it is composed of microservices as building blocks. This section does not distinguish between the characteristics that are specific to the microservices versus ones that are applicable to MSA. The intent is to identify the key characteristics of a solution that is architected as an MSA and, as such, consists of microservices.
Most of these characteristics are interrelated to some degree.
A key imperative for MSA is service independence. As business needs change and evolve, the impact to every service must be manageable. Service independence minimizes the impact to the service infrastructure by identifying and isolating those services that undergo constant churn. Once identified, these services should be upgradeable or replaceable without any additional changes to the software landscape. Since each service is also self-contained, redeployment of each independent service is simplified since there are no service dependencies to manage. Simpler service redeployments streamline release processes since fewer software assets need to be managed from release to release.
A microservice is independent of other microservices or other services. Instances of a microservice are independent of the other instances of the same microservice. Development and deployment of a microservice is independent of the development and deployment of other services.
Single responsibility, defined by MSA, is the direct alignment of a service to a singular business activity. For the purpose of this discussion, a business activity can be described as a unit of work performed by the organization that supports an existing business process or function. For single responsibility, the obligation of the service is to map completely to the business activity and deliver whatever business logic is necessary to fulfill the activity. Having each service associated with a single business activity enables the tracking of business change impact through the software service landscape. Impacted services can be more readily and easily identified when supporting a single business responsibility.
It is important to decompose the target business process to the right level to achieve the single responsibility, aligned to an atomic business activity, as well as to achieve the decoupled nature to support the resilience and scalability of the solution.
The decomposition can be guided by the scale cube and its three dimensions of scaling, particularly the y-axis scaling, which emphasizes the functional decomposition through splitting of applications into multiple microservices.
The other influencing concept is the bounded context from DDD, which can help determine the right level of decomposition through context mapping.
Self-containment dictates that a service shall encompass all external IT resources (e.g., data sources, business rules) necessary to support the business activity. In addition, self-containment necessitates that service dependencies falling outside the scope of the development team should be minimized or preferably eliminated. By adhering to self-containment, services are inherently more easily replaceable and upgradeable. Along with single responsibility, this characteristic also promotes single ownership of the service since it encapsulates not only specific business requirements (single responsibility) but also specific software dependencies (self-containment).
Microservices are packaged with containers and components as single-deployment units.
The self-containment principle does not remove the need for orchestration between microservices. The orchestration function is moved to the application and user interaction layer. Hence, to achieve self-containment, proper granularity in decomposition is essential.
In order to maintain minimal service dependencies, microservices must be highly decoupled. To achieve this decoupling, the business function must be capable of being decomposed down to the level where a microservice is implementing a single atomic business function. It is this decomposition, and the consequent removal of dependencies between the atomic business functions, that permits the service independence and self-containment required in an MSA.
By relying heavily on existing protocols whose sole purpose is to route messages (HTTP, for example), the needs of a service can be reduced to receiving the message request, applying the appropriate business logic, and generating a message response. MSA discourages the use of external data transformation or protocol bridging services since they introduce additional tightly-coupled service dependencies that will need to be managed during upgrades. MSA endorses the concept of smart endpoints, where all logic needed to manage the incoming request and produce an appropriate response remains encapsulated within the service.
An MSA must be a highly-resilient architecture. Its microservices must be designed for potential failures because individual service failures should not impinge negatively on the user experience. Since a microservice represents a single responsibility and is self-contained, a service failure could mean that a given business function or process is unable to complete successfully. Lengthy service downtime could also have a significant impact on the entire business. Thus, mechanisms must be in place to ensure timely service recovery. Real-time service monitoring can provide a proactive means for identifying services that are struggling under heavy load or unable to satisfy existing Service-Level Agreements (SLAs). Real-time monitoring of business metrics also provides insight to future business activity changes that will ripple down to IT services. Further, because of the independence of the underlying microservice, new instantiations will be automatically deployed immediately to allow for business continuity without the resolution of the failure itself.
Decentralized Data Management
Data management is performed by individual services, which are not managed or choreographed in the performance of data updates. Data consistency is achieved eventually, rather than instantaneously.
The implementation can support multiple development platforms (e.g., Java®, C++, JS, Ruby, Python, etc.) and technologies, and can be deployed in any of the containers or virtual machines that support these development platforms.
The focus here is on the solution and not each individual microservice. Individual microservices may not be implementation-agnostic, but the solution is.
Scalability and Resilience through Parallelism
Though each microservice will fail, the solution is highly resilient. This is achieved by instantiating multiple instances of a microservice in parallel.
This ability to deploy multiple parallel instances results in elasticity and scalability. The number of parallel instances can be increased or decreased to meet the workload demands.
For this characteristic, instrumentation and monitoring as listed below is a requirement.
Well-Defined Interface with a Published Contract
The interface (API) for a microservice is well-defined and published (i.e., available to the general developer community). This API is consistent across MSA implementations, to encourage re-use and avoid breakage (point-to-point integration).
Allows Independent Governance
This is consistent with the idea of a single team ownership for the cradle-to-grave lifecycle of a microservice. Such a team owns every aspect of its microservices including governance. Hence, governance may be decentralized and autonomous.
Single Team End-to-End Ownership
One team will own all aspects of a microservice. Its governance, development, testing, deployment, and operations. This is a DevOps model.
Instrumentation to Support Elasticity and Resilience
An MSA should have provision for instrumentation. An MSA must have the ability to monitor the services and dynamically create instances as needed.
Elasticity is the ability of a system to autonomously and dynamically adapt its capacity to handle varying workloads. Proper instrumentation and monitoring ensure the overall solution is resilient and scalable.
A microservice is independent of all other services.
Independence of services enables rapid service development and deployment, and permits scalability through instantiation of parallel, independent services. This characteristic also provides resilience; a microsservice is allowed to fail and its responsibilities are taken over by parallel instantiations (of the same microservice), which do not depend on other services. When a microservice fails, it does not bring down other services.
Both design and runtime independence of services are required. It is necessary for the business to determine whether providing scalability and resilience of the business function are paramount considerations. If so, MSA provides a means of achieving these characteristics.
A microservice focuses on one task only and on doing it well. A microservice focuses on delivering a small specific business capability.
This develops ideas including the principle of Single responsibility, Open-closed, Liskov substitution, Interface segregation, and Dependency inversion (SOLID), which is a tenet of Object-Oriented Design (OOD) and the core business domain and bounded context of DDD.
Microservices are aligned to atomic business functions, which can be modified and deployed independently. To achieve this it is critical that each microservice caters to a single functional responsibility.
This requires business function decomposition into atomic functional services and data exchanges.
A microservice is a self-contained, independent deployable unit.
In order for a microservice to be independent, it needs to include all necessary building blocks for its operation, or there will be dependencies to external systems and services.
This has architecture, design, implementation, and deployment implications.