The microservices definition discussed earlier in this chapter is arbitrary. Evangelists and practitioners have strong, but sometimes, different opinions on microservices. There is no single, concrete, and universally accepted definition for microservices. However, all successful microservices implementations exhibit a number of common characteristics. Therefore, it is important to understand these characteristics rather than sticking to theoretical definitions. Some of the common characteristics are detailed in this section.
Characteristics of microservices
Services are first class citizens
In the microservices world, services are first class citizens. Microservices expose service endpoints as APIs and abstract all their realization details. The internal implementation logic, architecture, and technologies, including programming language, database, quality of services mechanisms, and more, are completely hidden behind the service API.
Moreover, in the microservices architecture, there is no more application development, instead organizations will focus on service development. In most enterprises, this requires a major cultural shift in the way applications are built.
In a customer profile microservice, the internals, such as data structure, technologies, business logic, and so on, will be hidden. It wont be exposed or visible to any external entities. Access will be restricted through the service endpoints or APIs. For instance, customer profile microservices may expose register customer and get customers as two APIs for others to interact.
Characteristics of service in a microservice
Since microservices are more or less like a flavor of SOA, many of the service characteristics defined in the SOA are applicable to microservices as well.
The following are some of the characteristics of services that are applicable to microservices as well:
- Service contract: Similar to SOA, microservices are described through well-defined service contracts. In the microservices world, JSON and REST are universally accepted for service communication. In case of JSON/REST, there are many techniques used to define service contracts. JSON Schema, WADL, Swagger, and RAML are a few examples.
- Loose coupling: Microservices are independent and loosely coupled. In most cases, microservices accept an event as input and respond with another event. Messaging, HTTP, and REST are commonly used for interaction between microservices. Message-based endpoints provide higher levels of decoupling.
- Service abstraction: In microservices, service abstraction is not just abstraction of service realization, but also provides complete abstraction of all libraries and environment details, as discussed earlier.
- Service reuse: Microservices are course grained reusable business services. These are accessed by mobile devices and desktop channels, other microservices, or even other systems.
- Statelessness: Well-designed microservices are a stateless, shared nothing with no shared state, or conversational state maintained by the services. In case there is a requirement to maintain state, they will be maintained in a database, perhaps in-memory.
- Services are discoverable: Microservices are discoverable. In a typical microservices environment, microservices self-advertise their existence and make themselves available for discovery. When services die, they automatically take themselves out from the microservices ecosystem.
- Service interoperability: Services are interoperable as they use standard protocols and message exchange standards. Messaging, HTTP, and more are used as the transport mechanism. REST/JSON is the most popular method to develop interoperable services in the microservices world. In cases where further optimization is required on communications, then other protocols such as Protocol Buffers, Thrift, Avro, or Zero MQ could be used. However, use of these protocols may limit the overall interoperability of the services.
- Service Composeability: Microservices are composeable. Service composeability is achieved either through service orchestration or service choreography.
More details on SOA principles can be found at http://serviceorientation.com/serviceorientation/index.
Microservices are lightweight
Well-designed microservices are aligned to a single business capability; therefore, they perform only one function. As a result, one of the common characteristics we see in most of the implementations are microservices with smaller footprints.
When selecting supporting technologies, such as web containers, we will have to ensure that they are also lightweight so that the overall footprint remains manageable. For example, Jetty or Tomcat are better choices as application containers for microservices as compared to more complex traditional application servers, such as Weblogic or WebSphere.
Container technologies such as Docker also helps us keep the infrastructure footprint as minimal as possible compared to hypervisors such as VMware or Hyper-V.
As shown in the preceding diagram, microservices are typically deployed in Docker containers, which encapsulate the business logic and needed libraries. This helps us quickly replicate the entire setup on a new machine, a completely different hosting environment, or even move across different cloud providers. Since there is no physical infrastructure dependency, containerized microservices are easily portable.
Microservices with polyglot architecture
Since microservices are autonomous and abstract everything behind the service APIs, it is possible to have different architectures for different microservices. A few common characteristics that we see in microservices implementations are as follows:
- Different services use different versions of the same technologies. One microservice may be written on Java 1.7 and another one could be on Java 1.8.
- Different languages for developing different microservices, such as one microservice in Java and another one in Scala.
- Different architectures such as one microservice using Redis cache to serve data while another microservice could use MySQL as a persistent data store.
A polyglot language scenario is depicted in the following diagram:
In the preceding example, since Hotel Search is expected to have high transaction volumes with stringent performance requirements, it is implemented using Erlang. In order to support predictive search, Elastic Search is used as the data store. At the same time, Hotel Booking needs more ACID transactional characteristics. Therefore, it is implemented using MySQL and Java. The internal implementations are hidden behind service endpoints defined as REST/JSON over HTTP.
Automation in microservices environment
Most of the microservices implementations are automated to a maximum, ranging from development to production.
Since microservices break monolithic applications into a number of smaller services, large enterprises may see a proliferation of microservices. Large numbers of microservices are hard to manage until and unless automation is in place. The smaller footprint of microservices also helps us automate the microservices development to deployment life cycle. In general, microservices are automated end to end, for example, automated builds, automated testing, automated deployment, and elastic scaling:
As indicated in the diagram, automations are typically applied during the development, test, release, and deployment phases.
Different blocks in the preceding diagram are explained as follows:
- The development phase will be automated using version control tools, such as Git, together with continuous integration (CI) tools, such as Jenkins, Travis CI, and more. This may also include code quality checks and automation of unit testing. Automation of a full build on every code check-in is also achievable with microservices.
- The testing phase will be automated using testing tools such as Selenium, Cucumber, and other AB testing strategies. Since microservices are aligned to business capabilities, the number of test cases to automate will be fewer compared to the monolithic applications; hence, regression testing on every build also becomes possible.
- Infrastructure provisioning will be done through container technologies, such as Docker, together with release management tools, such as Chef or Puppet, and configuration management tools, such as Ansible. Automated deployments are handled using tools such as Spring Cloud, Kubernetes, Mesos, and Marathon.
Microservices with a supporting ecosystem
Most of the large scale microservices implementations have a supporting ecosystem in place. The ecosystem capabilities include DevOps processes, centralized log management, service registry, API gateways, extensive monitoring, service routing, and flow control mechanisms:
Microservices work well when supporting capabilities are in place, as represented in the preceding diagram.
Microservices are distributed and dynamic
Successful microservices implementations encapsulate logic and data within the service. This results in two unconventional situations:
- Distributed data and logic
- Decentralized governance
Compared to traditional applications, which consolidate all logic and data into one application boundary, microservices decentralize data and logic. Each service, aligned to a specific business capability, owns its own data and logic:
The dotted line in the preceding diagram implies the logical monolithic application boundary. When we migrate this to microservices, each microservice, A, B, and C, creates its own physical boundaries.
Microservices don't typically use centralized governance mechanisms the way they are used in SOA. One of the common characteristics of microservices implementations are that they are not relaying on heavyweight enterprise-level products, such as an Enterprise Service Bus (ESB). Instead, the business logic and intelligence are embedded as a part of the services themselves.
A retail example with ESB is shown as follows:
A typical SOA implementation is shown in the preceding diagram. Shopping Logic is fully implemented in the ESB by orchestrating different services exposed by Customer, Order, and Product. In the microservices approach, on the other hand, shopping itself will run as a separate microservice, which interacts with Customer, Product, and Order in a fairly decoupled way.
SOA implementations are heavily relaying on static registry and repository configurations to manage services and other artifacts. Microservices bring a more dynamic nature into this. Hence, a static governance approach is seen as an overhead in maintaining up-to-date information. This is why most of the microservices implementations use automated mechanisms to build registry information dynamically from the runtime topologies.
Antifragility, fail fast, and self healing
Antifragility is a technique successfully experimented with at Netflix. It is one of the most powerful approaches to build fail-safe systems in modern software development.
The antifragility concept is introduced by Nassim Nicholas Taleb in his book, Antifragile: Things That Gain from Disorder.
In the antifragility practice, software systems are consistently challenged. Software systems evolve through these challenges, and, over a period of time, get better and better to withstand these challenges. Amazon's Game Day exercise and Netflix's Simian Army are good examples of such antifragility experiments.
Fail Fast is another concept used to build fault-tolerant, resilient systems. This philosophy advocates systems that expect failures versus building systems that never fail. Importance has to be given to how quickly the system can fail, and, if it fails, how quickly it can recover from that failure. With this approach, the focus is shifted from Mean Time Between Failures (MTBF) to Mean Time To Recover (MTTR). A key advantage of this approach is that if something goes wrong, it kills itself, and the downstream functions won’t be stressed.
Self-Healing is commonly used in microservices deployments, where the system automatically learns from failures and adjusts itself. These systems also prevent future failures.