Microservices are an architectural style where applications are broken into small, independent services, each responsible for a single business function.
They are built as self-contained units, deployed independently, and communicate through APIs or messaging systems. Applications can be composed from microservices in many different patterns, ranging from simple API-driven setups to complex event-driven ecosystems.
Microservices are as opposed to monolithic code bases when as everything run in the same place. As a result, everything goes down when one aspect fails.
Definition: Microservices are small, loosely coupled services that each handle a specific business capability (e.g., authentication, payments, inventory).
Key Principles:
- Single Responsibility Principle (SRP): Each service does one thing well.
- Independent Deployability: Services can be updated or redeployed without affecting others.
- Decentralized Data Management: Each service manages its own database rather than sharing one.
- Polyglot Flexibility: Different services can be written in different languages or frameworks.
Microservices allow the following much better then monolithic applications:
- Separation of concerns
- Scalability
- Virtualisation and elasticity
How Microservices Are Built
This is now not so easy to put into practise. You might think that it can just be endless horizontal scaling but this is not how systems work in reality.
It is very important to think about the following 4 pillars of micro-services:
1. Dependencies
Since services depend on each other, failures can cascade. This is why there is a key requirement to build in latency and fault-tolerance. Additionally, a library running on one service that has a lot of dependencies to run might end up taking a lot of heap space and perform really poorly.
Here is how we deal with this:
- A library such as Hystrix to handle failures gracefully
- Better testing (we don’t want to test all possible combinations because that can lead to a combinatoral explosion)
- CAP Theorem: In the presence of a network partition, you must choose between consistency and availability. The choice is between eventual consistency and guaranteed consistency.
- Mutli-region strategy
2. Scale
To deal with scale, we need to understand what a stateless service is. It is something that does not store data (database or cache) so it can be scaled easily. This is what you can put in auto-scaling groups. So loosing an individual nodes has no cost.
This is also an argument to make services stateless so that more units can be managed by auto-scaling.
The issue for scale some with stateful services (databases and caches). The loss of a node is costly because there is a loss of data: it is not possible to create another on easily.
This single point of failure is an issue for scale. The key principle is that redundancy in fundamental.
The solution is to maintain multiple copies of these stateful services. A solution that can be used for this purpose is EVCache or Cassandra.
3. Variance
We are talking about variety in architecture. It is important to study what role this has on the software.
One way variance can be introduced is operational drift. This can happen when changes are not made across all services when improvements are made.
The way to overcome this is to automate the process of introducing changes so that there is a robust way to avoid introducing variance.
Polyglots and Containers are ways in which we can introduce intentional variance. This allows us to achieve better separation of concern to fight the monolithic pattern.
However, this means that a lot more tooling needs to be built to handle these containers, base docker images and node management. Additionally, there is a learning curve for everyone working on the services.
4. Change
The goal is to achieve velocity with confidence. We need to be able to make changes without breaking the entire system.
Here is why there is a need to use platforms such as Spinnaker which allows integrated, automated testing.
Roadmap
When you are going to build a microservice system, this is rough set of processes that you need to go through to come up with good design:
- Service Design
- Define clear boundaries (e.g., user service, order service).
- Choose appropriate technology stack per service.
- Communication
- Synchronous: REST/HTTP APIs, gRPC.
- Asynchronous: Message brokers (Kafka, RabbitMQ).
- Data Management
- Each service has its own database (SQL, NoSQL).
- Event sourcing or CQRS patterns for consistency.
- Deployment
- Containerization (Docker).
- Orchestration (Kubernetes).
- CI/CD pipelines for automated builds and deployments.
- Cross-Cutting Concerns
- API Gateway for routing, authentication, logging.
- Service discovery (e.g., Consul, Eureka).
- Monitoring and observability (Prometheus, Grafana).
There are a lot of open source tools that will help you implement a lot of these things so be sure to do research on what is available before reinventing the wheel.
Putting together microservices
There is no one way to put together different micro-services: it depends on the use case. These are a set of different approach you can potential use, but be sure to weight up their costs and benefits.
| Pattern | Description | Use Case |
|---|---|---|
| API Gateway | Single entry point routes requests to services | Web/mobile apps needing unified access |
| Direct API Calls | Services call each other via REST/gRPC | Simple systems with limited services |
| Event-Driven | Services publish/subscribe to events via broker | Real-time apps (e.g., notifications, IoT) |
| Service Mesh | Layer for managing service-to-service communication | Large-scale apps needing traffic control |
| Orchestration | Central controller coordinates workflows | Complex business processes (e.g., order fulfillment) |
| Choreography | Services react to events without central control | Decentralized workflows (e.g., e-commerce checkout) |
| Saga Pattern | Distributed transactions managed across services | Financial apps needing consistency |
| Backend for Frontend (BFF) | Separate backends tailored for each client type | Different UIs (mobile vs. desktop) |
| Composite UI | Frontend assembles data from multiple services | Dashboards pulling from diverse sources |
Risks & Challenges
- Data consistency issues due to decentralized databases.
- Operational complexity in managing many services.
- Network overhead from frequent inter-service communication.
- Security concerns with multiple exposed endpoints.