Microservices Design Principles
Microservices Design Principles
Get a crash course in understanding microservices and the difficulties in implementing them.
Join the DZone community and get the full member experience.Join For Free
Learn how to migrate and modernize stateless applications and run them in a Kubernetes cluster.
The objective of this post is to understand microservices, relevant software architecture, design principles and the constraints to be considered while developing microservices.
Microservices are small autonomous systems that provide a solution that is unique, distinct within the eco-system. It runs as a full-stack module and collaborates with other microservices that are part of the eco-system. Sam Newman defines microservices are "Small, focused and doing one thing very well" in his book "Building Microservices."
Microservices are created by slicing and dicing a single large monolithic system into many independent autonomous systems. It can also be a plug-gable add-on component to work along with the existing system as a new component or as a green field project.
Though the concept of microservice is not new, the evolution of cloud technologies, agile methodologies, continuous integration and automatic provisioning (Dev Ops) tools lead to the evolution of microservices.
2.1 Cloud Technologies
One of the important feature of cloud is "Elasticity". Cloud allows the user to dynamically scale up and scale down the performance and capacity of a system by dynamically increasing or decreasing the infrastructure resources such as virtual machines, storage, data base, etc. If the software is one single large monolithic system it cannot effectively utilize this capability of the cloud infrastructure, because the inner sub modules and communications pipe across the system could be the bottle neck, which could not scale appropriately.
Since the microservices are small, independent and full stack systems, it can efficiently use the elastic nature of the cloud infrastructure. By increasing or decreasing the number of instance of a microservice will directly impact the performance and capacity of the system proportionately.
2.2 Dev Ops
Dev Ops is a methodology focuses on speeding up the process of software development to customer deployment. This methodology concentrates on improving the communication and collaboration between the software development and IT operations by integration, automation and cooperation.
Microservices architecture supports to meet both software engineers and IT professionals objective. Being small and independent component it is relatively easier to develop, test, deploy and recovery (if failure) when compared to large monolithic architectures.
2.3 Agile Methodologies
Agile is the software development process model evolved from Extreme Programming (XP) and Iterative-and-Incremental (2I) development process models. Agile is best suitable for small teams working on software deliverable where the requirement volatility is high and time to market is shorter.
As per the agile manifesto, agile prefers:
- Individual interactions over Process and Tools
- Working Software over comprehensive documentation
- Customer Collaboration over contract negotiation
- Responding to Change over following a plan
A small dynamic team which works in agile process model developing a microservice that is small, independent and full-stack application will have a complete product ownership with clear boundaries of responsibility.
3. Design of Microservices
3.1 Characteristics of Microservices
Microservices are designed to be small, stateless, in(ter)dependent & full-stack application so that it could be deployed in cloud infrastructure.
Small: Microservices are designed to be small. But defining "small" is subjective. Some of the estimation techniques like lines of code, function points, use cases may be used. But they are not recommended estimation techniques in agile.
In the book Building Microservices the author Sam Newman suggest few techniques to define the size of microservice, they are : It should be small enough to be owned by a small agile development team, re-writable within one or two agile sprints ( typically two to four weeks) or the complexity does not require to refactoring or require further divide into another microservice.
Stateless: A stateless application handles every request with the information contained only within it. Microservices must be stateless and it must service the request without remembering the previous communications from the external system.
In(ter)dependent: Microservices must service the request independently, it may collaborate with other microservices within the eco-system. For example, a microservice that generates a unique report after interacting with other microservices is an interdependent system. In this scenario, other microservices which only provide the necessary data to reporting microservices may be independent services.
Full-Stack Application: A full stack application is individually deploy-able. It has its own server, network & hosting environment. The business logic, data model and the service interface (API / UI) must be part of the entire system. Microservice must be a full stack application.
3.2 Architecture Principles
Though SOA is one of the important architecture style helps in designing microservices. There are few more architecture styles and design principles need to be considered while designing microservices. They are:
3.2.1 Single Responsibility Principle (Robert C Martin)
Each microservice must be responsible for a specific feature or a functionality or aggregation of cohesive functionality. The thump rule to apply this principle is: "Gather those things which change for the same reason, Separate those things which change for the different reason".
3.2.2 Domain Driven Design
Domain driven design is an architectural principle in-line with object oriented approach. It recommends designing systems to reflect the real world domains. It considers the business domain, elements and behaviors and interactions between business domains. For example, in banking domain, individual microservices can be designed to handle various business functions such as retail banking, on-line banking, on-line trading etc. The retail banking microservice can offer services related to that eg. Open a bank account, cash withdraw, cash deposits, etc.
3.2.3 Service Oriented Architecture
The Service Oriented Architecture (SOA) is an architecture style, which enforces certain principles and philosophies. Following are the principles of SOA to be adhered while designing microservices for cloud.
The services must encapsulate the internal implementation details, so that the external system utilizes the services need not worry about the internals. Encapsulation reduces the complexity and enhances the flexibility (adaptability to change) of the system.
188.8.131.52 Loose Coupling
The changes in one microsystem should have zero or minimum impact on other services in the eco-system. This principle also suggests having a loosely coupled communication methods between the microservices. As per SOA, RESTful APIs are more suitable than Java RMI, where the later enforces a technology on other microservices.
184.108.40.206 Separation of Concern
Develop the microservices based on distinct features with zero overlap with other functions. The main objective is to reduce the interaction between services so that they are highly cohesive and loosely coupled. If we separate the functionality across wrong boundaries will lead tight coupling and increased complexity between services.
The above core principles of SOA provided only a gist of SOA. There are more principles and philosophies of SOA which nicely fits into design principles of microservices for cloud.
3.2.4 Hexagonal Architecture
This architecture style is proposed by Alistair Cockburn. It allows an application to equally driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. This also called as "Ports-Adapters Architecture", where the ports and adapters encapsulate the core application to function unanimously to external requests. The ports and adapters handle the external messages and convert them into appropriate functions or methods exposed by the inner core application. A typical microservice exposes RESTful APIs for external communication, message broker interface (eg. RabbitMQ, HornetQ, etc) for event notification and database adapters for persistence makes hexagonal architecture as a most suitable style for microservice development.
Though there are many architectural styles & principles the above items have high relevant to microservices.
4 Design Constraints
The design constraints (non-functional requirements) are the important decision makers while designing microservices. The success of a system is completely depends on Availability, Scalability, Performance, Usability and Flexibility.
The golden rule for availability says, anticipate failures and design accordingly so that the systems will be available for 99.999% (Five Nines). It means the system can go down only for a 5.5 minutes for an entire year. The cluster model is used to support high availability, where it suggests 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 & full stack will help us to run multiple instances in parallel in active-active or active-standby mode.
Microservices must be scale-able both horizontally and vertically. Being horizontally scale-able, we can have multiple instances of the microservice to increase the performance of the system. The design of the microservices must support horizontal scaling (scale-out).
Also microservices should be scale-able vertically (scale-in). If a microservice is hosted in a system with medium configuration such AWS EC2 t2-small (1-core, 2-GB memory) is moved to M4 10x-large ( 40 core & 160GB memory) it should scale accordingly. Similarly downsizing the system capacity must also be possible.
Performance is measured by throughput, response time (eg. 2500 TPS -transactions per second). The performance requirements must be available in the beginning of the design phase itself. There are technologies and design choices will affect the performance. They are:
- Synchronous or Asynchronous communication
- Blocking or Non-blocking APIs
- RESTful API or RPC
- XML or JSON , choice of
- SQL or NoSQL
- HornetQ or RabbitMQ
- MongoDB or Cassandra or CouchDB
So, appropriate technology and design decisions must be taken, to avoid re-work in the later stage.
Usability aspects of the design focuses on hiding the internal design, architecture, technology and other complexities to the end user or other system. Most of the time, microservices expose APIs to the end user as well as to other microservices. So, the APIs must be designed in a normalized way, so that it is easy to achieve the required services with minimal number of API calls.
Flexibility measures the adaptability to change. In the microservices eco-system, where each microservice is owned by different teams and developed in agile methodology, change will happen faster than any other systems. The microservices may not inter-operate if they don't adapt or accommodate the change in other systems. So, there must be a proper mechanism in place to avoid such scenarios which could include publishing the APIs, documenting the functional changes, clear communication plans.
This briefly summarizes the important design constraints for microservices.
5. New Problem Spaces
Though there are many positives with microservices, it can create some new challenges.
5.1 Complete Functional Testing
The end to end functional testing will be a great challenge in microservices environment, because we might need to deploy many microservices to validate single business functionality. Each microservice might have its own way of installation and configuration.
5.2 Data Integrity across the eco-system
Microservice systems run independently and asynchronously, they communicate each other through proper protocols or APIs. This could result in data integrity issues momentarily or out-of-sync due to failures. So we might need additional services to monitor the data integrity issues.
5.3 Increased Complexity
The complexity increases many folds, when a single monolithic is split into ten to twenty microservices and introduction of load balance server, monitoring, logging and auditing servers in to the eco-systems increases the operational overhead. Also the competency needed to manage and deploy the microservices becomes very critical, where the IT admins and DevOps engineers need to be aware of plethora of technologies used by independent agile development teams.
The articles "Microservices - Not a free lunch !" and Service Disoriented Architecture clearly warns us to be aware of issues with microservices, though they greatly support and favour this architecture style.
Microservices architecture style offers many advantages and we discussed it is most suitable for cloud infrastructure, speed up the deployment and recovery, minimizes the damages in case of failures. This article consolidates the needed knowledge areas in design, architecture and design constraints for designing microservices. Thank you.
Opinions expressed by DZone contributors are their own.