Microservices Architecture (aka Microservice Architecture) is an architectural approach emerging from service-oriented architecture. Sometimes called just microservices, the approach emphasizes team autonomy, ownership and comparatively small development and deployment size as a means to improve software agility, scalability, complexity, velocity, and time to market. In essence, microservices are an approach to solution decomposition as described in the Y Axis of the AKF Scale Cube..
The approach decomposes or disintegrates an application into multiple services. Each microservice (or micro-service) should have the following attributes:
- independently deployed (or capable of independent deployment)
- independently executed (not dependent on another service for execution)
- “owns” (or is responsible for creating) some unique business capability and its associated outcomes
- owned by a single team (no two teams own a microservice)
- “owns” its own data store or persistence - and ideally the only solution accessing that store
The above attributes help to achieve the following benefits when architectures and organizations are properly aligned:
- faster deployments and faster time to market for any capability
- higher team agility through lower inter-team dependence
- lower deployment associated risk
- higher team accountability and empowerment to achieve business outcomes
- independent service scalability and independent cost of scale per service
- when avoiding major anti-patterns, better and more reliable availability
- flexibility to apply the best tools to any outcome(language, technology, etc)
- higher team morale when teams are constructed cross functionally and with appropriate empowerment
The “micro” in micro-service is an unfortunate one. Too often it is seen as meaning “a single task” or “very, very small”. The best microservice architectures have services of varying size, each of them being the “right” size to achieve the benefits above. This article helps to explain the most important criteria for determining the size of any service. Team size, rate of change, desired availability, cost of availability, desired scalability, cost of scale, necessary skill sets, cost of development and data architecture are all important in determining service size.
Loose coupling is an essential characteristic of microservices. Any microservice should be capable of independent deployment. There must be zero coordination necessary for the deployment with other microservices, and other teams. This loose coupling enables frequent and rapid deployments, decreasing time to market for value creation within a product.
Each microservice is scaled by running multiple instances of it as in the X axis of the AKF Scale Cube. There are many processes to handle, and memory and CPU requirements are an important consideration when assessing the cost of operation of the entire system. Container technologies are often employed to aid with ease of deployment and service management.
It is possible (but not always advisable) to create a microservice system in which each service uses a different language and stack (polyglot implementations). Such a polyglot implementation has many advantages and disadvantages. Generally speaking, in smaller companies economy of scale, code reuse, and developer skills all set an upper bound on this number of no more than 2 to 3 “stacks”.
Microservices aren’t a panacea. They come with or invite certain disadvantages including:
- Too many coding languages – Polyglot implementations are a double-edged sword. Too many languages, in the end, could make your solution unwieldy and potentially difficult to maintain.
- Integration – Teams need to make a conscious effort to ensure their services are as loosely coupled as possible. Synchronous service to service interaction is an anti-pattern as it decreases availability and increases the cost of integration.
- Integration test – Testing monoliths is sometimes simpler than testing microservices. The spread of services across environments can make the construction of test environments a difficult task - automation is key.
- Communication – When service to service communication cannot be avoided, failures between microservices will occur. Versioning of services and service interfaces is critical to achieve low risk, high velocity deployments that minimize deployment related failures.
- Unique Failures - Microservices can introduce unique failure modes such as deadlock and race conditions. Teams need to take great care to think through these possibilities when defining service boundaries.
- Multiplicative Effect of Failure - Deployment architectures are important for microservices, as chaining services together will cause a multiplicative effect of failure that reduces downtime. When developing deployment architectures, choose services in breadth and libraries for depth to increase availability and reduce failure probability. Peruse our patterns and anti-patterns list for a better understanding of what to do and what not to do with microservices.
The following anti-patterns, if not properly managed, can significantly negatively impact the availability, scalability, latency, and cost of operations of a microservices solution.
- Calls in Series - increase latency and decrease availability
- Service Fuse - decreases availability
- Data Fuse - decreases availability and decreases developer velocity
- Service Fan Out- increases latency and decreases availability
- Data Fan Out - increases latency, decreases availability and provides no developer productivity improvements
- Service Mesh - the worst possible aggregation of all the anti-patterns above.
The following is a list of popular microservice patterns, including helpful tips as to how and when to use them and perhaps more importantly what to avoid when using the pattern:
- BackEnd for FrontEnd (BFF) Pattern - useful for solving the needs of multiple access modalities
- Smart End Points, Dumb Pipes - appropriate to help implement messaging for microservices
- Strangler Pattern - useful for migrating to microservices from a monolith
- Circuit Breaker Pattern - aids in increasing availability of a microservice by detecting latency related faults
- Bulkhead Pattern - creates fault isolation between microservices and increases overall availability
- CQRS Pattern - a useful pattern to implement the X axis of the AKF Scale Cube
- Sidecar Pattern - a useful pattern for functionality that multiple services need like monitoring
Microservice architectures allow teams to break solutions into easily owned components. This ownership helps to reduce the overhead necessary in coordinating releases, and in so doing increases release velocity and decreases time to market for value creation.
AKF Partners has helped to architect some of the most scalable, highly available, fault-tolerant and fastest response time solutions on the internet. Give us a call - we can help.