A microservice architecture is the natural consequence of applying the single responsibility principle at architectural level. Within a microservice architecture the functionality is decomposed in a set of collaborating services and the scale cube is applied. Services communicate with each other either using synchronous protocols like HTTP/REST or asynchronous protocols such as AMQP/JMS. In this way, business domain concepts are modelled as resources with one or more of these managed by a microservice. Since a business request can span multiple microservices separated by network partitions, it is important to consider possible failures in the system. Techniques such as timeouts, circuit breakers, bulkheads can help to maintain overall system uptime in spite of outage.
Often microservices display similar internal structure consisting of some or all of the layers as shown below.
Normally services are developed independently from another. Typically, a team will act as guardian to one or more microservices,
Each service has its own storage in order to decouple from other services. When necessary, consistency between services is maintained using application events.
Following this approach has a number of benefits:
There are however also a number of drawbacks:
A challenge is deciding how to partition the system into microservices. One approach is to partition services by use case. We could for example have a micro service shipping within a partitioned e-commerce application that is responsible for shipping completed orders. Another approach is to partition by entity. This kind of service is responsible for all operations that operate on entities of a given type.
Ideally, each service should have only a small set of responsibilities. The Single Responsible Principle states that every class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class. It makes sense to apply this principle to microservice design as well.
There isn’t a really straight forward approach regarding the naming of microservices but the following guidelines should help in creating a consistency in the naming of the microservices.
Naming conventions may seem trivial at first, but as the number of microservices grow, so will the potential to reuse. In larger organizations, this means that more and more architects, analysts, and developers are discovering and then incorporating foreign services within their solution designs. The effort required to establish a consistent level of clarity across all microservices pays off quickly when interoperability and reuse opportunities are more easily recognized and seized.
Non-functional requirements are important decision makers while designing micro services. The success of a system is largely dependent on its availability, scalability, performance, usability and flexibility.
The golden rule for availability says, anticipate failures and design accordingly so that the system will be available for 99.999%. It means that the system can only go down for 5.5 minutes a year. The cluster model is used to support such high availability (having group of services run in active-active mode or active-standby model).
So while designing microservices, it must be designed for appropriate clustering and high availability model. The basic properties of microservices such as stateless, independent and full stack will help to run multiple instances in parallel.
Microservices must be scalable both horizontally and vertically. Being horizontally scalable means we can have multiple instances of the microservices to increase the performance of the system. The design of the microservices must support horizontal scaling (scale-out)
Also scaling vertically should be possible. If a microservice is hosted on a medium capacity and moved to a higher capacity the performance of the service should scale accordingly. Similarly downsizing the system capacity must be possible.
Performance is measured by throughput vs response time (TPS - Transactions Per Second). The performance requirements must be available in the beginning of the design phase itself. There are technologies and design choices that will affect the performance and to avoid rework at a later stage these should be known.
Usability aspects of the design focus on hiding the internal design, architecture, technology and other complexities to the end user of the system. Most of the time, microservices expose APIs to the end user, so the APIs must be designed in a normalized way so that it is easy to achieve the required functionality with a minimal number of API calls.
Flexibility measures the adaptability to change. In the microservices eco-system, where each microservice is owned (possibly) by different teams and developed in agile methodology changes will happen faster than any other system. The microservices may not inter-operate if they don’t adapt or accommodate to changes in other systems. So there must be a proper mechanism in place to publish changes to API, functional changes, etc.